import { Equater, MaybeAsyncEquater } from "./types.js"; export interface EqualitySet extends Iterable { readonly size: number; add(obj: T): boolean; contains(obj: T): boolean; remove(obj: T): boolean; clear(): void; values(): IterableIterator; } class NativeEqualitySet implements EqualitySet { readonly #set = new Set(); get size() { return this.#set.size; } add(obj: T) { const exists = this.contains(obj); this.#set.add(obj); return !exists; } contains(obj: T) { return this.#set.has(obj); } remove(obj: T) { return this.#set.delete(obj); } clear() { this.#set.clear(); } values() { return this.#set.values(); } [Symbol.iterator]() { return this.#set[Symbol.iterator](); } } class CustomEqualitySet implements EqualitySet { readonly #list: T[] = []; readonly #equater: Equater; constructor(equater: Equater) { this.#equater = equater; } get size() { return this.#list.length; } add(obj: T) { if (this.contains(obj)) { return false; } this.#list.push(obj); return true; } contains(obj: T) { for (const val of this.#list) { if (this.#equater(obj, val)) { return true; } } return false; } remove(obj: T) { const length = this.#list.length; for (let i = 0; i < length; i++) { if (this.#equater(obj, this.#list[i])) { this.#list.splice(i, 1); return true; } } return false; } clear() { this.#list.length = 0; } values() { return this[Symbol.iterator](); } [Symbol.iterator]() { return this.#list[Symbol.iterator](); } } export function createEqualitySet(equater?: Equater): EqualitySet { return equater ? new CustomEqualitySet(equater) : new NativeEqualitySet(); } export interface AsyncEqualitySet extends Iterable { readonly size: number; add(obj: T): Promise; contains(obj: T): Promise; remove(obj: T): Promise; clear(): void; values(): IterableIterator; } class NativeAsyncEqualitySet implements AsyncEqualitySet { readonly #set = new Set(); get size() { return this.#set.size; } async add(obj: T) { const exists = await this.contains(obj); this.#set.add(obj); return !exists; } async contains(obj: T) { return this.#set.has(obj); } async remove(obj: T) { return this.#set.delete(obj); } clear() { this.#set.clear(); } values() { return this.#set.values(); } [Symbol.iterator]() { return this.#set[Symbol.iterator](); } } class CustomAsyncEqualitySet implements AsyncEqualitySet { readonly #list: T[] = []; readonly #equater: MaybeAsyncEquater; constructor(equater: MaybeAsyncEquater) { this.#equater = equater; } get size() { return this.#list.length; } async add(obj: T) { if (await this.contains(obj)) { return false; } this.#list.push(obj); return true; } async contains(obj: T) { for (const val of this.#list) { if (await this.#equater(obj, val)) { return true; } } return false; } async remove(obj: T) { const length = this.#list.length; for (let i = 0; i < length; i++) { if (await this.#equater(obj, this.#list[i])) { this.#list.splice(i, 1); return true; } } return false; } clear() { this.#list.length = 0; } values() { return this[Symbol.iterator](); } [Symbol.iterator]() { return this.#list[Symbol.iterator](); } } export function createAsyncEqualitySet(equater?: MaybeAsyncEquater): AsyncEqualitySet { return equater ? new CustomAsyncEqualitySet(equater) : new NativeAsyncEqualitySet(); }