import { ErrFrom, ErrValue, OkFrom, OkValue, Option, OptionPromise, Result, ResultLike, ResultMapOption, ResultMapOrElse, ResultMapResult, ResultPromise, ResultPromiseLike, ResultPromiseMapOption, ResultPromiseMapOrElse, ResultPromiseMapResult, Some, UnwrapableResult, } from "./mod"; /** * Test that a variable implements the {@linkcode Option} interface * @returns true if variable can be cast to `Option` */ export function isResult( possibleResult: unknown, ): possibleResult is Result { return possibleResult instanceof ResultValue; } export function isResultPromise( possibleResult: unknown, ): possibleResult is ResultPromise { return possibleResult instanceof PromisedResult; } export function isResultLike( possibleResult: unknown, ): possibleResult is ResultLike { return isResult(possibleResult) || isResultPromise(possibleResult); } /** * Creates a `Result` with an `Ok` value of type T. * * When the value is {@linkcode ResultLike}, the actual return value is that. * When the value is a Promise to T, the return value implements {@linkcode ResultPromise} */ export function Ok(ok: T): OkFrom { return ( isResult(ok) ? ok : isResultPromise(ok) ? ok : (ok instanceof OkValue) || (ok instanceof ErrValue) ? ResultValue.from(ok) : ok instanceof Promise ? PromisedResult.from(ok) : ResultValue.from(OkValue.from(ok)) ) as OkFrom; } /** * Create an {@linkcode ResultPromise} from a value. * @example * ```typescript * declare function calculate(n: number): ResultPromise; * * Ok(42) * .mapResult( * // when using Ok here, the compiler will error on calculate with an Argument Error * () => OkPromise(-1), * calculate * ); * ``` */ export function OkPromise(value: T): ResultPromise { return Ok(Promise.resolve(value)) as ResultPromise; } /** * Creates a `Result` with an `Err` value of type E. * * When the value is {@linkcode ResultLike}, the actual return value is that. * When the value is a Promise to E, the return value implements {@linkcode ResultPromise} */ export function Err(err: E): ErrFrom { return ((err instanceof ResultValue || err instanceof PromisedResult) ? err : (err instanceof OkValue) || (err instanceof ErrValue) ? ResultValue.from(err) : err instanceof Promise ? PromisedResult.from(err, Err) : ResultValue.from(ErrValue.from(err) as ResultValue)) as ErrFrom< T, E >; } /** * Create an {@linkcode ResultPromise} from a value. * @example * ```typescript * declare function calculate(n: number): ResultPromise; * * Ok(42) * .mapResult( * // when using Err here, the compiler will error on calculate with an Argument Error * () => ErrPromise("could not calculate"), * calculate * ); * ``` */ export function ErrPromise(err: E): ResultPromise { return Err(Promise.resolve(err)) as unknown as ResultPromise; } export class ResultValue implements Result, UnwrapableResult { result: UnwrapableResult; constructor( result: UnwrapableResult, ) { this.result = result; } get type(): symbol { return this.result.type; } static from(result: UnwrapableResult): Result { return new ResultValue(result); } and(res: Result): Result { return this.result.and(res); } andThen(op: (value: T) => ResultPromiseLike): ResultPromise; andThen(op: (value: T) => Result): Result; andThen(fn: (value: T) => ResultLike): ResultLike { return this.result.andThen(fn as (some: T) => Result); } err(): Option { return this.result.err(); } isOk(): boolean { return this.result.isOk(); } isErr(): boolean { return this.result.isErr(); } map(fn: (value: T) => Promise): PromisedResult; map(fn: (value: T) => U): Result; map( fn: (value: T) => U | Promise, ): PromisedResult | Result { return this.result.map(fn as (some: T) => U); } mapErr(fn: (err: E) => Promise): ResultPromise; mapErr(fn: (err: E) => F): Result; mapErr( fn: (err: E) => F | Promise, ): Result | ResultPromise { return this.result.mapErr(fn as (err: E) => F); } mapResult( def: (err: E) => U, fn: (value: T) => U, ): ResultMapResult { return this.result.mapResult(def, fn); } mapOption( def: (err: E) => U, fn: (value: T) => U, ): ResultMapOption { return this.result.mapOption(def, fn); } mapOrElse( def: (err: E) => U, fn: (value: T) => U, ): ResultMapOrElse { return this.result.mapOrElse(def, fn); } ok(): Option { return this.result.ok(); } or(optb: Result): Result { return this.result.or(optb); } orElse(fn: (err: E) => Promise>): PromisedResult; orElse(fn: (err: E) => Result): Result; orElse( fn: (err: E) => Result | Promise>, ): PromisedResult | Result { return this.result.orElse(fn as (err: E) => Result); } unwrap(): T { return this.result.unwrap(); } unwrapOr(def: T): T { return this.result.unwrapOr(def); } unwrapOrElse(def: (err: E) => T): T; unwrapOrElse(def: (err: E) => Promise): T | Promise; unwrapOrElse(def: (err: E) => T | Promise): T | Promise { return this.result.unwrapOrElse(def as (err: E) => T); } [Symbol.iterator]() { return this.result[Symbol.iterator](); } } type Creator = ((v: T) => OkFrom) | ((e: E) => OkFrom); export class PromisedResult implements ResultPromise { private promise: Promise>; constructor( promise: Promise>, creator: Creator, ) { this.promise = promise.then( (v) => isResultLike(v) ? v : (creator as unknown as (value: Result) => Result)(v), (err) => Err(err as E), ) as Promise>; } get [Symbol.toStringTag](): string { return `PromisedResult`; //this.promise[Symbol.toStringTag]; } static from( promise: Promise>, then: Creator = Ok, ): ResultPromise { return isResultPromise(promise) ? promise : new PromisedResult(promise, then); } then, TResult2 = never>( onfulfilled?: | ((value: Result) => TResult1 | PromiseLike) | undefined | null, onrejected?: | ((reason: unknown) => TResult2 | PromiseLike) | undefined | null, ): Promise { return this.promise.then(onfulfilled, onrejected); } catch( onrejected?: | ((reason: unknown) => TResult | PromiseLike) | null | undefined, ): Promise | TResult> { return this.promise.catch(onrejected); } finally(onfinally?: (() => void) | null | undefined): Promise> { return this.promise.finally(onfinally); } and(res: Result): ResultPromise { return Ok( this.promise.then((result) => result.and(res)), ); } andThen(fn: (value: T) => ResultLike): ResultPromise { return Ok( this.promise.then((result) => result.andThen(fn as (value: T) => Result) ), ) as ResultPromise; } err(): OptionPromise { return Some( this.promise.then((result) => result.err()) as Promise>, ); } isOk(): Promise { return this.promise.then((result) => result.isOk()); } isErr(): Promise { return this.promise.then((result) => result.isErr()); } map(fn: (value: T) => Promise): ResultPromise; map(fn: (value: T) => U): ResultPromise; map(fn: unknown): ResultPromise { return Ok( this.promise.then((result) => result.map(fn as (value: T) => Promise)), ); } mapErr(fn: (err: E) => Promise): ResultPromise; mapErr(fn: (err: E) => F): ResultPromise; mapErr( fn: (err: E) => F | Promise, ): Result | ResultPromise { return Err( this.promise.then((result) => result.mapErr(fn as (err: E) => Promise) ), ); } mapResult( def: (err: E) => U, fn: (value: T) => U, ): ResultPromiseMapResult { return Ok( this.promise.then((result) => result.mapResult(def, fn) as Promise> ), ) as ResultPromiseMapResult; } mapOption( def: (err: E) => U, fn: (value: T) => U, ): ResultPromiseMapOption { return Some( this.promise.then( (result) => result.mapOption(def, fn), ) as Promise>, ) as unknown as ResultPromiseMapOption; } mapOrElse( def: (err: E) => U, fn: (value: T) => U, ): ResultPromiseMapOrElse { return this.promise.then( (result) => result.mapOrElse(def, fn) as U, ) as ResultPromiseMapOrElse; } ok(): OptionPromise { return Some( this.promise.then((result) => result.ok()) as Promise>, ); } or(optb: Result): ResultPromise { return Ok( this.promise.then((result) => result.or(optb)), ); } orElse(fn: (err: E) => Promise>): ResultPromise; orElse(fn: (err: E) => Result): ResultPromise; orElse( fn: (err: E) => Result | Promise>, ): ResultPromise { return Ok( this.promise.then((result) => { return result.orElse(fn as (err: E) => Promise>); }), ); } unwrapOr(def: T): Promise { return this.promise.then((result) => result.unwrapOr(def)); } unwrapOrElse(def: (err: E) => T): Promise; unwrapOrElse(def: (err: E) => Promise): Promise; unwrapOrElse(def: (err: E) => T | Promise): Promise { return this.promise.then((result) => result.unwrapOrElse(def as (err: E) => T) ); } }