import type { RadixTree, Entry, LeafType } from './types' /** @ignore */ const ENTRIES = 'ENTRIES' /** @ignore */ const KEYS = 'KEYS' /** @ignore */ const VALUES = 'VALUES' /** @ignore */ const LEAF = '' as LeafType interface Iterators { ENTRIES: Entry KEYS: string VALUES: T } type Kind = keyof Iterators type Result> = Iterators[K] type IteratorPath = { node: RadixTree, keys: string[] }[] export type IterableSet = { _tree: RadixTree, _prefix: string } /** * @private */ class TreeIterator> implements Iterator> { set: IterableSet _type: K _path: IteratorPath constructor (set: IterableSet, type: K) { const node = set._tree const keys = Array.from(node.keys()) this.set = set this._type = type this._path = keys.length > 0 ? [{ node, keys }] : [] } next (): IteratorResult> { const value = this.dive() this.backtrack() return value } dive (): IteratorResult> { if (this._path.length === 0) { return { done: true, value: undefined } } const { node, keys } = last(this._path)! if (last(keys) === LEAF) { return { done: false, value: this.result() } } const child = node.get(last(keys)!)! this._path.push({ node: child, keys: Array.from(child.keys()) }) return this.dive() } backtrack (): void { if (this._path.length === 0) { return } const keys = last(this._path)!.keys keys.pop() if (keys.length > 0) { return } this._path.pop() this.backtrack() } key (): string { return this.set._prefix + this._path .map(({ keys }) => last(keys)) .filter(key => key !== LEAF) .join('') } value (): T { return last(this._path)!.node.get(LEAF)! } result (): Result { switch (this._type) { case VALUES: return this.value() as Result case KEYS: return this.key() as Result default: return [this.key(), this.value()] as Result } } [Symbol.iterator] () { return this } } const last = (array: T[]): T | undefined => { return array[array.length - 1] } export { TreeIterator, ENTRIES, KEYS, VALUES, LEAF }