import type { Encoder } from "./codec"; import { canonicalEncode, canonicalString } from "./codec"; import { Tag } from "./constants"; import { FlexMap, FlexSet, _iterMap } from "./flex"; import { PreserveOn } from "./symbols"; import { stringify } from "./text"; import { DefaultPointer, Value } from "./values"; import { Bytes } from './bytes'; import { fromJS } from "./fromjs"; export type DictionaryType = 'Dictionary' | 'Set'; export const DictionaryType = Symbol.for('DictionaryType'); export class Dictionary extends FlexMap, V> { get [DictionaryType](): DictionaryType { return 'Dictionary'; } static isDictionary(x: any): x is Dictionary { return x?.[DictionaryType] === 'Dictionary'; } static fromJS(x: object): Dictionary, T> { if (Dictionary.isDictionary(x)) return x as Dictionary, T>; const d = new Dictionary, T>(); Object.entries(x).forEach(([key, value]) => d.set(key, fromJS(value))); return d; } constructor(items?: readonly [Value, V][]); constructor(items?: Iterable, V]>); constructor(items?: Iterable, V]>) { super(canonicalString, items); } mapEntries(f: (entry: [Value, V]) => [Value, W]): Dictionary { const result = new Dictionary(); for (let oldEntry of this.entries()) { const newEntry = f(oldEntry); result.set(newEntry[0], newEntry[1]) } return result; } asPreservesText(): string { return '{' + Array.from(_iterMap(this.entries(), ([k, v]) => k.asPreservesText() + ': ' + stringify(v))).join(', ') + '}'; } clone(): Dictionary { return new Dictionary(this); } toString(): string { return this.asPreservesText(); } get [Symbol.toStringTag]() { return 'Dictionary'; } [PreserveOn](encoder: Encoder) { if (encoder.canonical) { const pieces = Array.from(this).map(([k, v]) => Bytes.concat([canonicalEncode(k), canonicalEncode(v)])); pieces.sort(Bytes.compare); encoder.encoderawvalues(Tag.Dictionary, pieces); } else { encoder.emitbyte(Tag.Dictionary); this.forEach((v, k) => { encoder.push(k); encoder.push(v as unknown as Value); // Suuuuuuuper unsound }); encoder.emitbyte(Tag.End); } } } export class Set extends FlexSet> { get [DictionaryType](): DictionaryType { return 'Set'; } static isSet(x: any): x is Set { return x?.[DictionaryType] === 'Set'; } constructor(items?: Iterable>) { super(canonicalString, items); } map(f: (value: Value) => Value): Set { return new Set(_iterMap(this[Symbol.iterator](), f)); } filter(f: (value: Value) => boolean): Set { const result = new Set(); for (let k of this) if (f(k)) result.add(k); return result; } toString(): string { return this.asPreservesText(); } asPreservesText(): string { return '#{' + Array.from(_iterMap(this.values(), v => v.asPreservesText())).join(', ') + '}'; } clone(): Set { return new Set(this); } get [Symbol.toStringTag]() { return 'Set'; } [PreserveOn](encoder: Encoder) { if (encoder.canonical) { const pieces = Array.from(this).map(k => canonicalEncode(k)); pieces.sort(Bytes.compare); encoder.encoderawvalues(Tag.Set, pieces); } else { encoder.encodevalues(Tag.Set, this); } } }