import { Encoder, canonicalEncode, canonicalString } from "./encoder"; import { Tag } from "./constants"; import { FlexMap, FlexSet, _iterMap } from "./flex"; import { Value } from "./values"; import { Bytes } from './bytes'; import { GenericEmbedded } from "./embedded"; import type { Preservable } from "./encoder"; import type { Writer, PreserveWritable } from "./writer"; import { annotations, Annotated } from "./annotated"; export type DictionaryType = 'Dictionary' | 'Set'; export const DictionaryType = Symbol.for('DictionaryType'); export class KeyedDictionary, V, T = GenericEmbedded> extends FlexMap implements Preservable, PreserveWritable { get [DictionaryType](): DictionaryType { return 'Dictionary'; } static isKeyedDictionary, V, T = GenericEmbedded>(x: any): x is KeyedDictionary { return x?.[DictionaryType] === 'Dictionary'; } constructor(items?: readonly [K, V][]); constructor(items?: Iterable); constructor(items?: Iterable) { super(canonicalString, items); } mapEntries, R = GenericEmbedded>(f: (entry: [K, V]) => [S, W]): KeyedDictionary { const result = new KeyedDictionary(); for (let oldEntry of this.entries()) { const newEntry = f(oldEntry); result.set(newEntry[0], newEntry[1]) } return result; } clone(): KeyedDictionary { return new KeyedDictionary(this); } get [Symbol.toStringTag]() { return 'Dictionary'; } __preserve_on__(encoder: Encoder) { if (encoder.canonical) { const entries = Array.from(this); const pieces = entries.map<[Bytes, number]>(([k, _v], i) => [canonicalEncode(k), i]); pieces.sort((a, b) => Bytes.compare(a[0], b[0])); encoder.state.emitbyte(Tag.Dictionary); pieces.forEach(([_encodedKey, i]) => { const [k, v] = entries[i]; encoder.push(k); encoder.push(v as unknown as Value); // Suuuuuuuper unsound }); encoder.state.emitbyte(Tag.End); } else { encoder.state.emitbyte(Tag.Dictionary); this.forEach((v, k) => { encoder.push(k); encoder.push(v as unknown as Value); // Suuuuuuuper unsound }); encoder.state.emitbyte(Tag.End); } } __preserve_text_on__(w: Writer) { w.state.writeSeq('{', '}', this.entries(), ([k, v]) => { w.push(k); if (Annotated.isAnnotated(v) && (annotations(v).length > 1) && w.state.isIndenting) { w.state.pieces.push(':'); w.state.indentCount++; w.state.writeIndent(); w.push(v); w.state.indentCount--; } else { w.state.pieces.push(': '); w.push(v as unknown as Value); // Suuuuuuuper unsound } }); } } export class Dictionary> extends KeyedDictionary, V, T> { static isDictionary>(x: any): x is Dictionary { return x?.[DictionaryType] === 'Dictionary'; } static __from_preserve__(v: Value): undefined | Dictionary { return Dictionary.isDictionary(v) ? v : void 0; } } export class KeyedSet, T = GenericEmbedded> extends FlexSet implements Preservable, PreserveWritable { get [DictionaryType](): DictionaryType { return 'Set'; } static isKeyedSet, T = GenericEmbedded>(x: any): x is KeyedSet { return x?.[DictionaryType] === 'Set'; } constructor(items?: Iterable) { super(canonicalString, items); } map, R = GenericEmbedded>(f: (value: K) => S): KeyedSet { return new KeyedSet(_iterMap(this[Symbol.iterator](), f)); } filter(f: (value: K) => boolean): KeyedSet { const result = new KeyedSet(); for (let k of this) if (f(k)) result.add(k); return result; } clone(): KeyedSet { return new KeyedSet(this); } get [Symbol.toStringTag]() { return 'Set'; } __preserve_on__(encoder: Encoder) { if (encoder.canonical) { const pieces = Array.from(this).map<[Bytes, K]>(k => [canonicalEncode(k), k]); pieces.sort((a, b) => Bytes.compare(a[0], b[0])); encoder.encodevalues(Tag.Set, pieces.map(e => e[1])); } else { encoder.encodevalues(Tag.Set, this); } } __preserve_text_on__(w: Writer) { w.state.writeSeq('#{', '}', this, vv => w.push(vv)); } } export class Set extends KeyedSet, T> { static isSet(x: any): x is Set { return x?.[DictionaryType] === 'Set'; } static __from_preserve__(v: Value): undefined | Set { return Set.isSet(v) ? v : void 0; } }