import { Consumer, Kind, Peek, Supplier, Sync, UnaryFunction } from '@jamashita/anden-type'; import { Alive, DeadConstructor, Schrodinger } from '@jamashita/genitore-schrodinger'; import { Chrono } from './Chrono'; import { containsError, ISuperposition, SReturnType } from './ISuperposition'; import { SuperpositionError } from './SuperpositionError'; import { SuperpositionInternal } from './SuperpositionInternal'; export class Superposition implements ISuperposition { private readonly internal: ISuperposition; public static all(superpositions: Iterable>): Superposition, D> { const ss: Array> = [...superpositions]; if (ss.length === 0) { return Superposition.ofSchrodinger(Alive.of([])); } const promises: Array>> = ss.map((s: Superposition): Promise> => { return s.terminate(); }); return Superposition.of((chrono: Chrono, D>) => { ss.forEach((s: Superposition) => { chrono.catch(s.getErrors()); }); return Promise.all(promises).then((schrodingers: Array>) => { const s: Schrodinger, D> = Schrodinger.all(schrodingers); s.ifAlive((a: Array) => { chrono.accept(a); }); s.ifDead((err: D) => { chrono.decline(err); }); s.ifContradiction((cause: unknown) => { chrono.throw(cause); }); }); }); } public static anyway(superpositions: Iterable>): Promise>> { const promises: Array>> = [...superpositions].map((s: Superposition): Promise> => { return s.terminate(); }); return Promise.all(promises); } public static of(func: Consumer, D>>, ...errors: ReadonlyArray>): Superposition, D> { return Superposition.ofSuperposition(SuperpositionInternal.of(func, errors)); } public static ofSchrodinger(schrodinger: Schrodinger, D>, ...errors: ReadonlyArray>): Superposition, D> { return Superposition.of((chrono: Chrono, D>) => { chrono.catch(errors); if (schrodinger.isAlive()) { return chrono.accept(schrodinger.get()); } if (schrodinger.isDead()) { return chrono.decline(schrodinger.getError()); } if (schrodinger.isContradiction()) { return chrono.throw(schrodinger.getCause()); } return chrono.throw(new SuperpositionError('UNEXPECTED SCHRODINGER STATE')); }, ...errors); } public static ofSuperposition(superposition: ISuperposition): Superposition { return new Superposition(superposition); } public static playground(supplier: Supplier | PromiseLike>>, ...errors: ReadonlyArray>): Superposition, D> { return Superposition.of((chrono: Chrono, D>) => { try { const value: Exclude | PromiseLike> = supplier(); if (Kind.isPromiseLike(value)) { return value.then( (v: A) => { return chrono.accept(v as Exclude, Error>); }, (e: unknown) => { if (containsError(e, chrono.getErrors())) { return chrono.decline(e); } return chrono.throw(e); } ); } return chrono.accept(value as Exclude, Error>); } catch (err: unknown) { if (containsError(err, chrono.getErrors())) { return chrono.decline(err); } return chrono.throw(err); } }, ...errors); } protected constructor(internal: ISuperposition) { this.internal = internal; } public get(): Promise> { return this.internal.get(); } public getErrors(): Set> { return this.internal.getErrors(); } public ifAlive(consumer: Consumer>): this { this.internal.ifAlive(consumer); return this; } public ifContradiction(consumer: Consumer): this { this.internal.ifContradiction(consumer); return this; } public ifDead(consumer: Consumer): this { this.internal.ifDead(consumer); return this; } public map( mapper: UnaryFunction, SReturnType>, ...errors: Array> ): Superposition { return Superposition.ofSuperposition(this.internal.map(mapper, ...this.internal.getErrors(), ...errors)); } public pass( accepted: Consumer>, declined: Consumer, thrown: Consumer ): this { this.internal.pass(accepted, declined, thrown); return this; } public peek(peek: Peek): this { this.internal.peek(peek); return this; } public recover( mapper: UnaryFunction>, ...errors: Array> ): Superposition { return Superposition.ofSuperposition(this.internal.recover(mapper, ...errors)); } public serialize(): string { return this.internal.toString(); } public terminate(): Promise> { return this.internal.terminate(); } public toString(): string { return this.serialize(); } public transform( alive: UnaryFunction, SReturnType>, dead: UnaryFunction>, ...errors: Array> ): Superposition { return Superposition.ofSuperposition(this.internal.transform(alive, dead, ...errors)); } }