2021-03-02 21:54:42 +00:00
|
|
|
import { Encoder, canonicalEncode, canonicalString } from "./encoder";
|
2021-02-17 15:52:01 +00:00
|
|
|
import { Tag } from "./constants";
|
|
|
|
import { FlexMap, FlexSet, _iterMap } from "./flex";
|
|
|
|
import { PreserveOn } from "./symbols";
|
|
|
|
import { stringify } from "./text";
|
2021-03-02 21:43:10 +00:00
|
|
|
import { DefaultPointer, Value } from "./values";
|
2021-02-17 15:52:01 +00:00
|
|
|
import { Bytes } from './bytes';
|
2021-03-02 21:43:10 +00:00
|
|
|
import { fromJS } from "./fromjs";
|
2021-02-17 15:52:01 +00:00
|
|
|
|
|
|
|
export type DictionaryType = 'Dictionary' | 'Set';
|
|
|
|
export const DictionaryType = Symbol.for('DictionaryType');
|
|
|
|
|
2021-03-10 22:14:26 +00:00
|
|
|
export class KeyedDictionary<K extends Value<T>, V, T = DefaultPointer> extends FlexMap<K, V> {
|
2021-02-17 15:52:01 +00:00
|
|
|
get [DictionaryType](): DictionaryType {
|
|
|
|
return 'Dictionary';
|
|
|
|
}
|
|
|
|
|
2021-03-10 22:14:26 +00:00
|
|
|
static isKeyedDictionary<K extends Value<T>, V, T = DefaultPointer>(x: any): x is KeyedDictionary<K, V, T> {
|
2021-02-17 15:52:01 +00:00
|
|
|
return x?.[DictionaryType] === 'Dictionary';
|
|
|
|
}
|
|
|
|
|
2021-03-09 14:59:40 +00:00
|
|
|
constructor(items?: readonly [K, V][]);
|
|
|
|
constructor(items?: Iterable<readonly [K, V]>);
|
|
|
|
constructor(items?: Iterable<readonly [K, V]>) {
|
2021-02-22 19:00:15 +00:00
|
|
|
super(canonicalString, items);
|
2021-02-17 15:52:01 +00:00
|
|
|
}
|
|
|
|
|
2021-03-10 22:14:26 +00:00
|
|
|
mapEntries<W, S extends Value<R>, R = DefaultPointer>(f: (entry: [K, V]) => [S, W]): KeyedDictionary<S, W, R> {
|
2021-03-09 14:59:40 +00:00
|
|
|
const result = new KeyedDictionary<S, W, R>();
|
2021-02-17 15:52:01 +00:00
|
|
|
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]) =>
|
2021-03-10 22:14:26 +00:00
|
|
|
stringify(k) + ': ' + stringify(v))).join(', ') +
|
2021-02-17 15:52:01 +00:00
|
|
|
'}';
|
|
|
|
}
|
|
|
|
|
2021-03-09 14:59:40 +00:00
|
|
|
clone(): KeyedDictionary<K, V, T> {
|
|
|
|
return new KeyedDictionary(this);
|
2021-02-17 15:52:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
toString(): string {
|
|
|
|
return this.asPreservesText();
|
|
|
|
}
|
|
|
|
|
|
|
|
get [Symbol.toStringTag]() { return 'Dictionary'; }
|
|
|
|
|
|
|
|
[PreserveOn](encoder: Encoder<T>) {
|
|
|
|
if (encoder.canonical) {
|
2021-03-05 20:14:15 +00:00
|
|
|
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.emitbyte(Tag.Dictionary);
|
|
|
|
pieces.forEach(([_encodedKey, i]) => {
|
|
|
|
const [k, v] = entries[i];
|
|
|
|
encoder.push(k);
|
|
|
|
encoder.push(v as unknown as Value<T>); // Suuuuuuuper unsound
|
|
|
|
});
|
|
|
|
encoder.emitbyte(Tag.End);
|
2021-02-17 15:52:01 +00:00
|
|
|
} else {
|
|
|
|
encoder.emitbyte(Tag.Dictionary);
|
|
|
|
this.forEach((v, k) => {
|
|
|
|
encoder.push(k);
|
|
|
|
encoder.push(v as unknown as Value<T>); // Suuuuuuuper unsound
|
|
|
|
});
|
|
|
|
encoder.emitbyte(Tag.End);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-10 22:14:26 +00:00
|
|
|
export class Dictionary<V, T = DefaultPointer> extends KeyedDictionary<Value<T>, V, T> {
|
|
|
|
static isDictionary<V, T = DefaultPointer>(x: any): x is Dictionary<V, T> {
|
2021-03-09 14:59:40 +00:00
|
|
|
return x?.[DictionaryType] === 'Dictionary';
|
|
|
|
}
|
|
|
|
|
2021-03-10 22:14:26 +00:00
|
|
|
static fromJS<V = DefaultPointer, T = DefaultPointer>(x: object): Dictionary<Value<V>, T> {
|
2021-03-09 14:59:40 +00:00
|
|
|
if (Dictionary.isDictionary<Value<V>, T>(x)) return x as Dictionary<Value<V>, T>;
|
|
|
|
const d = new Dictionary<Value<V>, T>();
|
|
|
|
Object.entries(x).forEach(([key, value]) => d.set(key, fromJS(value)));
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-10 22:14:26 +00:00
|
|
|
export class KeyedSet<K extends Value<T>, T = DefaultPointer> extends FlexSet<K> {
|
2021-02-17 15:52:01 +00:00
|
|
|
get [DictionaryType](): DictionaryType {
|
|
|
|
return 'Set';
|
|
|
|
}
|
|
|
|
|
2021-03-10 22:14:26 +00:00
|
|
|
static isKeyedSet<K extends Value<T>, T = DefaultPointer>(x: any): x is KeyedSet<K, T> {
|
2021-02-17 15:52:01 +00:00
|
|
|
return x?.[DictionaryType] === 'Set';
|
|
|
|
}
|
|
|
|
|
2021-03-09 14:59:40 +00:00
|
|
|
constructor(items?: Iterable<K>) {
|
2021-02-22 19:00:15 +00:00
|
|
|
super(canonicalString, items);
|
2021-02-17 15:52:01 +00:00
|
|
|
}
|
|
|
|
|
2021-03-10 22:14:26 +00:00
|
|
|
map<S extends Value<R>, R = DefaultPointer>(f: (value: K) => S): KeyedSet<S, R> {
|
2021-03-09 14:59:40 +00:00
|
|
|
return new KeyedSet(_iterMap(this[Symbol.iterator](), f));
|
2021-02-17 15:52:01 +00:00
|
|
|
}
|
|
|
|
|
2021-03-09 14:59:40 +00:00
|
|
|
filter(f: (value: K) => boolean): KeyedSet<K, T> {
|
|
|
|
const result = new KeyedSet<K, T>();
|
2021-02-17 15:52:01 +00:00
|
|
|
for (let k of this) if (f(k)) result.add(k);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
toString(): string {
|
|
|
|
return this.asPreservesText();
|
|
|
|
}
|
|
|
|
|
|
|
|
asPreservesText(): string {
|
|
|
|
return '#{' +
|
2021-03-10 22:14:26 +00:00
|
|
|
Array.from(_iterMap(this.values(), stringify)).join(', ') +
|
2021-02-17 15:52:01 +00:00
|
|
|
'}';
|
|
|
|
}
|
|
|
|
|
2021-03-10 22:14:26 +00:00
|
|
|
clone(): KeyedSet<K, T> {
|
|
|
|
return new KeyedSet(this);
|
2021-02-17 15:52:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
get [Symbol.toStringTag]() { return 'Set'; }
|
|
|
|
|
|
|
|
[PreserveOn](encoder: Encoder<T>) {
|
|
|
|
if (encoder.canonical) {
|
2021-03-09 14:59:40 +00:00
|
|
|
const pieces = Array.from(this).map<[Bytes, K]>(k => [canonicalEncode(k), k]);
|
2021-03-05 20:14:15 +00:00
|
|
|
pieces.sort((a, b) => Bytes.compare(a[0], b[0]));
|
|
|
|
encoder.encodevalues(Tag.Set, pieces.map(e => e[1]));
|
2021-02-17 15:52:01 +00:00
|
|
|
} else {
|
|
|
|
encoder.encodevalues(Tag.Set, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-03-09 14:59:40 +00:00
|
|
|
|
2021-03-10 22:14:26 +00:00
|
|
|
export class Set<T = DefaultPointer> extends KeyedSet<Value<T>, T> {
|
|
|
|
static isSet<T = DefaultPointer>(x: any): x is Set<T> {
|
2021-03-09 14:59:40 +00:00
|
|
|
return x?.[DictionaryType] === 'Set';
|
|
|
|
}
|
|
|
|
}
|