import { Last } from 'ts-toolbelt/out/List/Last' import { Length } from 'ts-toolbelt/out/List/Length' import { Pop } from 'ts-toolbelt/out/List/Pop' import { Merge } from 'ts-toolbelt/out/Object/Merge' import { ListOf } from 'ts-toolbelt/out/Union/ListOf' /** * Handling iteration as if it were events. * * Every element "yield'd" from an iterator must be a binary tuple, * where the 1st element is a symbol (representing the event) and the 2nd * is the value "emitted". * * You may only have 1 handler for each event and the handler can return * something to the iterator if neccessary. * * @example * const Foo = Symbol('foo') * const Bar = Symbol('bar') * * type MyGenerator = Generator< * | [typeof Foo, string] * | [typeof Bar, number], * any, * undefined * > * * function* myGenerator(): MyGenerator { * yield [Foo, 'foo'] * yield [Bar, 123] * } * * const handler = IteratorHandler * .create(myGenerator()) * .handle(Foo, (value) => console.info(value)) * .handle(Bar, (num) => console.info(num)) * .run() */ export default class IteratorHandler { #iterator: Gen #handlers: Handlers static create(iterator: Gen) { return new IteratorHandler(iterator, {}) } constructor(iterator: Gen, handlers: Handlers) { this.#iterator = iterator this.#handlers = handlers } handle>( eventName: EventName, handler: Handler[EventName]> ) { return new IteratorHandler(this.#iterator, { ...this.#handlers, [eventName]: handler, }) } run() { let item = this.#iterator.next() while (!item.done) { item = this.#iterator.next( this.#emit( item.value[0] as keyof Events, item.value[1] as Events[keyof Events] ) ) } } #emit>( eventName: EventName, value: Events[EventName] ) { return this.#handlers[eventName]?.(value) } } type _Events< L extends readonly [symbol, unknown][], R extends Record > = Length extends 0 ? R : Last extends [infer K, infer V] ? K extends symbol ? _Events, Merge>> : never : never type Events = _Events, {}> type Handler = (value: T) => any type Handlers = Partial<{ [K in keyof Events]: Handler[K]> }> type Gen = Generator