import { Encoder, canonicalString } from "./encoder"; import { Tag } from "./constants"; import { FlexMap, FlexSet, _iterMap, IdentitySet } 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 type CompoundKey = Value | (Preservable & PreserveWritable); export class EncodableDictionary extends FlexMap implements Preservable, PreserveWritable { constructor( public readonly encodeK: (k: K) => CompoundKey, public readonly encodeV: (v: V) => CompoundKey, items?: readonly [K, V][] | Iterable ) { super((k: K) => canonicalString(encodeK(k)), items); } __preserve_on__(encoder: Encoder) { encodeDictionaryOn(this, encoder, (k, e) => e.push(this.encodeK(k)), (v, e) => e.push(this.encodeV(v))); } __preserve_text_on__(w: Writer) { writeDictionaryOn(this, w, (k, w) => w.push(this.encodeK(k)), (v, w) => w.push(this.encodeV(v))); } } export class KeyedDictionary, V, T = GenericEmbedded> extends EncodableDictionary { get [DictionaryType](): DictionaryType { return 'Dictionary'; } static isKeyedDictionary, V, T = GenericEmbedded>( x: any, ): x is KeyedDictionary { return x?.[DictionaryType] === 'Dictionary'; } constructor(items?: readonly [K, V][] | Iterable) { // The cast in encodeV is suuuuuuuper unsound, since V may not in fact be Encodable and Writable. // Don't try to encode/write dictionaries holding non-encodable/non-writable values. super(k => k, v => v as CompoundKey, 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'; } } 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 function encodeDictionaryOn( dict: Map, encoder: Encoder, encodeK: (k: K, encoder: Encoder) => void, encodeV: (v: V, encoder: Encoder) => void, ) { if (encoder.canonical) { const entries = Array.from(dict); const canonicalEncoder = new Encoder({ canonical: true, embeddedEncode: encoder.embeddedEncode, }); const pieces = entries.map<[Bytes, number]>(([k, _v], i) => { encodeK(k, canonicalEncoder); return [canonicalEncoder.contents(), i]; }); pieces.sort((a, b) => Bytes.compare(a[0], b[0])); encoder.grouped(Tag.Dictionary, () => pieces.forEach(([_encodedKey, i]) => { const [k, v] = entries[i]; encodeK(k, encoder); encodeV(v, encoder); })); } else { encoder.grouped(Tag.Dictionary, () => dict.forEach((v, k) => { encodeK(k, encoder); encodeV(v, encoder); })); } } export function writeDictionaryOn( dict: Map, w: Writer, writeK: (k: K, w: Writer) => void, writeV: (v: V, w: Writer) => void, ) { w.state.writeSeq('{', '}', dict.entries(), ([k, v]) => { writeK(k, w); if (Annotated.isAnnotated(v) && (annotations(v).length > 1) && w.state.isIndenting) { w.state.pieces.push(':'); w.state.indentCount++; w.state.writeIndent(); writeV(v, w); w.state.indentCount--; } else { w.state.pieces.push(': '); writeV(v, w); } }); } export class EncodableSet extends FlexSet implements Preservable, PreserveWritable { constructor( public readonly encodeV: (v: V) => CompoundKey, items?: Iterable, ) { super((v: V) => canonicalString(encodeV(v)), items); } __preserve_on__(encoder: Encoder) { encodeSetOn(this, encoder, (v, e) => e.push(this.encodeV(v))); } __preserve_text_on__(w: Writer) { writeSetOn(this, w, (v, w) => w.push(this.encodeV(v))); } } export class KeyedSet, T = GenericEmbedded> extends EncodableSet { get [DictionaryType](): DictionaryType { return 'Set'; } static isKeyedSet, T = GenericEmbedded>( x: any, ): x is KeyedSet { return x?.[DictionaryType] === 'Set'; } constructor(items?: Iterable) { super(k => k, 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'; } } 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; } } export function encodeSetOn( s: IdentitySet, encoder: Encoder, encodeV: (v: V, encoder: Encoder) => void, ) { if (encoder.canonical) { const canonicalEncoder = new Encoder({ canonical: true, embeddedEncode: encoder.embeddedEncode, }); const pieces = Array.from(s).map<[Bytes, V]>(v => { encodeV(v, canonicalEncoder); return [canonicalEncoder.contents(), v]; }); pieces.sort((a, b) => Bytes.compare(a[0], b[0])); encoder.grouped(Tag.Set, () => pieces.forEach(([_e, v]) => encodeV(v, encoder))); } else { encoder.grouped(Tag.Set, () => s.forEach(v => encodeV(v, encoder))); } } export function writeSetOn( s: IdentitySet, w: Writer, writeV: (v: V, w: Writer) => void, ) { w.state.writeSeq('#{', '}', s, vv => writeV(vv, w)); }