// deno-lint-ignore-file no-explicit-any import type { TaggedError } from "../error/tagged-error.ts"; import { Result } from "../result/result.ts"; import type { MaybePromise } from "../types/maybe-promise.ts"; import type { MergeTypes } from "../types/merge-types.ts"; export class Effect< TValue = any, TError extends TaggedError = never, TDeps = void, > { static from( fn: (deps: TDeps) => MaybePromise>, ): Effect { return new Effect(fn); } static tryFrom( fn: () => MaybePromise, errorMapper: (error: any) => TError, ): Effect { return new Effect( async () => { try { const value = await fn(); return Result.ok(value); } catch (error) { return Result.failWith(errorMapper(error)); } }, ); } static ok(): Effect; static ok(value: TValue): Effect; static ok(value?: TValue): Effect { return new Effect(() => Result.ok(value)); } static failWith( error: TError, ): Effect { return new Effect(() => Result.failWith(error)); } constructor( private readonly fn: ( deps: TDeps, ) => MaybePromise>, ) { } map( fn: (value: TValue) => MaybePromise, ): Effect { return new Effect(async (deps: TDeps) => { const result = await this.fn(deps); if (result.isError()) { return result; } return Result.ok(await fn(result.value as TValue)); }); } flatMap( fn: ( value: TValue, ) => Effect, ): Effect> { return new Effect(async (deps: TDeps & TNewDeps) => { const result = await this.fn(deps); if (result.isError()) { return result as Result; } return await fn(result.value as TValue).fn(deps); }) as Effect< TNewValue, TError | TNewError, MergeTypes >; } async run(deps: TDeps): Promise> { return await this.fn(deps); } async runOrThrow(deps: TDeps): Promise { return (await this.fn(deps)).unwrapOrThrow(); } async failOrThrow(deps: TDeps): Promise { return (await this.fn(deps)).unwrapErrorOrThrow(); } static seq< T1, TE1 extends TaggedError, T2, TE2 extends TaggedError, >( fn1: () => Effect, fn2: (value: T1) => Effect, ): Effect; static seq< T1, TE1 extends TaggedError, T2, TE2 extends TaggedError, T3, TE3 extends TaggedError, >( fn1: () => Effect, fn2: (value: T1) => Effect, fn3: (value: T2) => Effect, ): Effect; static seq< T1, TE1 extends TaggedError, T2, TE2 extends TaggedError, T3, TE3 extends TaggedError, T4, TE4 extends TaggedError, >( fn1: () => Effect, fn2: (value: T1) => Effect, fn3: (value: T2) => Effect, fn4: (value: T3) => Effect, ): Effect; static seq( ...fns: ((...args: any[]) => Effect)[] ): Effect { let result = fns[0]!(); for (let i = 1; i < fns.length; i++) { result = result.flatMap((value) => fns[i]!(value)); } return result; } } export type ExtractEffectDependencies = T extends Effect ? TDeps : never;