import { Arr, Function, KeyOfType, Tail } from "./types.ts"; import { map } from "./iter.ts"; export class Chain { run; constructor(run: () => T) { this.run = run; } static from(value: T) { return new Chain(() => [value] as [T]); } to(map: Function<[...T, ...A], O>, ...args: A) { const this_run = this.run; return new Chain(() => [map(...this_run(), ...args)] as [O]); } front(...values: I) { const this_run = this.run; return new Chain(() => [...values, ...this_run()] as [...I, ...T]); } back(...values: I) { const this_run = this.run; return new Chain(() => [...this_run(), ...values] as [...T, ...I]); } nth(index: Index) { const this_run = this.run; return new Chain(() => [this_run()[index]] as T[Index]); } call< A extends Arr, Arg extends [...Tail, ...A], M extends KeyOfType>, O extends ReturnType, >(method: M, ...args: A) { const this_run = this.run; return new Chain(() => { const [instance, ...rest] = this_run(); return [instance[method](...rest, ...args)] as [O]; }); } call_ignore< A extends Arr, Arg extends [...Tail, ...A], M extends KeyOfType>, >(method: M, ...args: A) { const this_run = this.run; return new Chain(() => { const res1: T = this_run(); const [instance, ...rest] = res1; instance[method](...rest, ...args); return res1; }); } call_keep< A extends Arr, Arg extends [...Tail, ...A], M extends KeyOfType>, O extends ReturnType, >(method: M, ...args: A) { const this_run = this.run; return new Chain(() => { const res1 = this_run(); const [instance, ...rest] = res1; const res2: O = instance[method](...rest, ...args); return [...res1, res2] as [...T, O]; }); } construct< A extends Arr, C extends new (...args: [...T, ...A]) => unknown, O extends InstanceType, >(class_: C, ...args: A) { const this_run = this.run; return new Chain(() => [new class_(...this_run(), ...args)] as [O]); } } Deno.test("example", async () => { const { assertEquals } = await import("https://deno.land/std@0.224.0/assert/mod.ts"); function times_2(num: number) { return num * 2; } // type: number const [_o1] = Chain.from(3).to(times_2).run(); // type: number const [_o2] = Chain.from(3).front(2, 5).to(Math.max).run(); function parse_and_add(text: string, num: number) { return parseInt(text) + num; } // type: string const [_o3] = Chain.from(3).front("5").to(parse_and_add).call("toString").run(); // type: Set const [_o4] = Chain.from([1, 2, 3]) .call_keep("push", 7) .construct(Set) .call_ignore("add", 8) .to(map, (item: number) => item.toString()) .run(); assertEquals([..._o4], ["1", "2", "3", "7", "8"]); });