import { Consumer, Peek, Reject, Resolve, UnaryFunction } from '@jamashita/anden-type'; import { DestroyPassPlan, DestroyPlan, MapPassPlan, MapPlan, Plan, RecoveryPassPlan, RecoveryPlan, SpoilPlan } from '@jamashita/genitore-plan'; import { Alive, Contradiction, Dead, DeadConstructor, Schrodinger, Still } from '@jamashita/genitore-schrodinger'; import { Chrono } from './Chrono'; import { ISuperposition, SReturnType } from './ISuperposition'; import { AlivePlan, CombinedChronoPlan, DeadPlan, DestroyChronoPlan, MapChronoPlan, RecoveryChronoPlan } from './Plan'; export class SuperpositionInternal implements ISuperposition, Chrono { private schrodinger: Schrodinger; private readonly plans: Set, D>>; private readonly errors: Set>; public static of(func: Consumer>, errors: Iterable>): SuperpositionInternal { return new SuperpositionInternal(func, errors); } protected constructor(func: Consumer>, errors: Iterable>) { this.schrodinger = Still.of(); this.plans = new Set(); this.errors = new Set(errors); func(this); } public accept(value: Exclude): void { if (this.settled()) { return; } this.schrodinger = Alive.of(value); this.plans.forEach((plan: MapPlan>) => { plan.onMap(value); }); } public catch(errors: Iterable>): void { [...errors].forEach((error: DeadConstructor) => { this.errors.add(error); }); } public decline(error: D): void { if (this.settled()) { return; } this.schrodinger = Dead.of(error); this.plans.forEach((plan: RecoveryPlan) => { plan.onRecover(error); }); } public get(): Promise> { return new Promise((resolve: Resolve>, reject: Reject) => { this.pass( (value: Exclude) => { resolve(value); }, (value: D) => { reject(value); }, (e: unknown) => { reject(e); } ); }); } public getErrors(): Set> { return new Set(this.errors); } private handle(map: MapPlan>, recover: RecoveryPlan, destroy: DestroyPlan): unknown { if (this.schrodinger.isAlive()) { return map.onMap(this.schrodinger.get()); } if (this.schrodinger.isDead()) { return recover.onRecover(this.schrodinger.getError()); } if (this.schrodinger.isContradiction()) { return destroy.onDestroy(this.schrodinger.getCause()); } return this.plans.add(CombinedChronoPlan.of(map, recover, destroy)); } public ifAlive(consumer: Consumer>): this { this.handle(MapPassPlan.of(consumer), SpoilPlan.of(), SpoilPlan.of()); return this; } public ifContradiction(consumer: Consumer): this { this.handle(SpoilPlan.of(), SpoilPlan.of(), DestroyPassPlan.of(consumer)); return this; } public ifDead(consumer: Consumer): this { this.handle(SpoilPlan.of(), RecoveryPassPlan.of(consumer), SpoilPlan.of()); return this; } public map( mapper: UnaryFunction, SReturnType>, ...errors: Array> ): SuperpositionInternal { return SuperpositionInternal.of((chrono: Chrono) => { return this.handle(AlivePlan.of(mapper, chrono), RecoveryChronoPlan.of(chrono), DestroyChronoPlan.of(chrono)); }, [...this.errors, ...errors]); } public pass(accepted: Consumer>, declined: Consumer, thrown: Consumer): this { this.handle(MapPassPlan.of(accepted), RecoveryPassPlan.of(declined), DestroyPassPlan.of(thrown)); return this; } public peek(peek: Peek): this { this.handle(MapPassPlan.of(peek), RecoveryPassPlan.of(peek), DestroyPassPlan.of(peek)); return this; } public recover( mapper: UnaryFunction>, ...errors: Array> ): SuperpositionInternal { return SuperpositionInternal.of((chrono: Chrono) => { return this.handle(MapChronoPlan.of(chrono), DeadPlan.of(mapper, chrono), DestroyChronoPlan.of(chrono)); }, errors); } public serialize(): string { return this.schrodinger.toString(); } private settled(): boolean { return this.schrodinger.isAlive() || this.schrodinger.isDead() || this.schrodinger.isContradiction(); } public terminate(): Promise> { return new Promise((resolve: Resolve>) => { this.peek(() => { resolve(this.schrodinger); }); }); } public throw(cause: unknown): void { if (this.settled()) { return; } this.schrodinger = Contradiction.of(cause); this.plans.forEach((plan: DestroyPlan) => { plan.onDestroy(cause); }); } public toString(): string { return this.serialize(); } public transform( alive: UnaryFunction, SReturnType>, dead: UnaryFunction>, ...errors: Array> ): SuperpositionInternal { return SuperpositionInternal.of((chrono: Chrono) => { this.handle(AlivePlan.of(alive, chrono), DeadPlan.of(dead, chrono), DestroyChronoPlan.of(chrono)); }, errors); } }