import { Equater } from "./sync.js"; import { MaybeAsyncEquater } from "./async.js"; import { asAsyncGenerator } from "./utils.js"; export interface EqualityMap extends Iterable<[K, V]> { get(key: K): V | undefined; set(key: K, value: V): V | undefined; contains(key: K): boolean; remove(key: K): V | undefined; clear(): void; } class NativeEqualityMap implements EqualityMap { readonly #map = new Map(); get(key: K) { return this.#map.get(key); } set(key: K, value: V) { const existing = this.get(key); this.#map.set(key, value); return existing; } contains(key: K) { return this.#map.has(key); } remove(key: K) { const existing = this.get(key); this.#map.delete(key); return existing; } clear() { this.#map.clear(); } [Symbol.iterator]() { return this.#map[Symbol.iterator](); } } class CustomEqualityMap implements EqualityMap { readonly #list: [K, V][] = []; readonly #keyComparer: Equater; constructor(keyComparer: Equater) { this.#keyComparer = keyComparer; } get(key: K) { for (const entry of this.#list) { if (this.#keyComparer(key, entry[0])) { return entry[1]; } } return undefined; } set(key: K, value: V) { for (const entry of this.#list) { if (this.#keyComparer(key, entry[0])) { const previous = entry[1]; entry[1] = value; return previous; } } this.#list.push([key, value]); return undefined; } contains(key: K) { for (const entry of this.#list) { if (this.#keyComparer(key, entry[0])) { return true; } } return false; } remove(key: K) { for (let i = 0; i < this.#list.length; i++) { if (this.#keyComparer(key, this.#list[i][0])) { const removed = this.#list.splice(i, 1); return removed[0][1]; } } return undefined; } clear() { this.#list.length = 0; } [Symbol.iterator]() { return this.#list[Symbol.iterator](); } } export function createEqualityMap(keyComparer?: Equater): EqualityMap { return keyComparer ? new CustomEqualityMap(keyComparer) : new NativeEqualityMap(); } export interface AsyncEqualityMap extends AsyncIterable<[K, V]> { get(key: K): Promise; set(key: K, value: V): Promise; contains(key: K): Promise; remove(key: K): Promise; clear(): Promise; } class NativeAsyncEqualityMap implements AsyncEqualityMap { readonly #map = new Map(); async get(key: K) { return this.#map.get(key); } async set(key: K, value: V) { const existing = await this.get(key); this.#map.set(key, value); return existing; } async contains(key: K) { return this.#map.has(key); } async remove(key: K) { const existing = await this.get(key); this.#map.delete(key); return existing; } async clear() { this.#map.clear(); } [Symbol.asyncIterator]() { return asAsyncGenerator(this.#map[Symbol.iterator]()); } } class CustomAsyncEqualityMap implements AsyncEqualityMap { readonly #list: [K, V][] = []; readonly #keyComparer: MaybeAsyncEquater; constructor(keyComparer: MaybeAsyncEquater) { this.#keyComparer = keyComparer; } async get(key: K) { for (const entry of this.#list) { if (await this.#keyComparer(key, entry[0])) { return entry[1]; } } return undefined; } async set(key: K, value: V) { for (const entry of this.#list) { if (await this.#keyComparer(key, entry[0])) { const previous = entry[1]; entry[1] = value; return previous; } } this.#list.push([key, value]); return undefined; } async contains(key: K) { for (const entry of this.#list) { if (await this.#keyComparer(key, entry[0])) { return true; } } return false; } async remove(key: K) { for (let i = 0; i < this.#list.length; i++) { if (await this.#keyComparer(key, this.#list[i][0])) { const removed = this.#list.splice(i, 1); return removed[0][1]; } } return undefined; } async clear() { this.#list.length = 0; } [Symbol.asyncIterator]() { return asAsyncGenerator(this.#list[Symbol.iterator]()); } } export function createAsyncEqualityMap(keyComparer?: MaybeAsyncEquater): AsyncEqualityMap { return keyComparer ? new CustomAsyncEqualityMap(keyComparer) : new NativeAsyncEqualityMap(); }