forked from eemeli/yaml
/
YAMLSeq.ts
128 lines (116 loc) · 3.79 KB
/
YAMLSeq.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import type { BlockSequence, FlowCollection } from '../parse/cst.js'
import type { Schema } from '../schema/Schema.js'
import type { StringifyContext } from '../stringify/stringify.js'
import { stringifyCollection } from '../stringify/stringifyCollection.js'
import { Collection } from './Collection.js'
import { isScalar, ParsedNode, Range, SEQ } from './Node.js'
import type { Pair } from './Pair.js'
import { isScalarValue, Scalar } from './Scalar.js'
import { toJS, ToJSContext } from './toJS.js'
export declare namespace YAMLSeq {
interface Parsed<
T extends ParsedNode | Pair<ParsedNode, ParsedNode | null> = ParsedNode
> extends YAMLSeq<T> {
items: T[]
range: Range
srcToken?: BlockSequence | FlowCollection
}
}
export class YAMLSeq<T = unknown> extends Collection {
static get tagName(): 'tag:yaml.org,2002:seq' {
return 'tag:yaml.org,2002:seq'
}
items: T[] = []
constructor(schema?: Schema) {
super(SEQ, schema)
}
add(value: T): void {
this.items.push(value)
}
/**
* Removes a value from the collection.
*
* `key` must contain a representation of an integer for this to succeed.
* It may be wrapped in a `Scalar`.
*
* @returns `true` if the item was found and removed.
*/
delete(key: unknown): boolean {
const idx = asItemIndex(key)
if (typeof idx !== 'number') return false
const del = this.items.splice(idx, 1)
return del.length > 0
}
/**
* Returns item at `key`, or `undefined` if not found. By default unwraps
* scalar values from their surrounding node; to disable set `keepScalar` to
* `true` (collections are always returned intact).
*
* `key` must contain a representation of an integer for this to succeed.
* It may be wrapped in a `Scalar`.
*/
get(key: unknown, keepScalar: true): Scalar<T> | undefined
get(key: unknown, keepScalar?: boolean): T | undefined
get(
key: unknown,
keepScalar?: boolean
): T | Scalar<T> | undefined {
const idx = asItemIndex(key)
if (typeof idx !== 'number') return undefined
const it = this.items[idx]
return !keepScalar && isScalar<T>(it) ? it.value : it
}
/**
* Checks if the collection includes a value with the key `key`.
*
* `key` must contain a representation of an integer for this to succeed.
* It may be wrapped in a `Scalar`.
*/
has(key: unknown): boolean {
const idx = asItemIndex(key)
return typeof idx === 'number' && idx < this.items.length
}
/**
* Sets a value in this collection. For `!!set`, `value` needs to be a
* boolean to add/remove the item from the set.
*
* If `key` does not contain a representation of an integer, this will throw.
* It may be wrapped in a `Scalar`.
*/
set(key: unknown, value: T): void {
const idx = asItemIndex(key)
if (typeof idx !== 'number')
throw new Error(`Expected a valid index, not ${key}.`)
const prev = this.items[idx]
if (isScalar(prev) && isScalarValue(value)) prev.value = value
else this.items[idx] = value
}
toJSON(_?: unknown, ctx?: ToJSContext) {
const seq: unknown[] = []
if (ctx?.onCreate) ctx.onCreate(seq)
let i = 0
for (const item of this.items) seq.push(toJS(item, String(i++), ctx))
return seq
}
toString(
ctx?: StringifyContext,
onComment?: () => void,
onChompKeep?: () => void
): string {
if (!ctx) return JSON.stringify(this)
return stringifyCollection(this, ctx, {
blockItemPrefix: '- ',
flowChars: { start: '[', end: ']' },
itemIndent: (ctx.indent || '') + ' ',
onChompKeep,
onComment
})
}
}
function asItemIndex(key: unknown): number | null {
let idx = isScalar(key) ? key.value : key
if (idx && typeof idx === 'string') idx = Number(idx)
return typeof idx === 'number' && Number.isInteger(idx) && idx >= 0
? idx
: null
}