import { Optional, UnpackOptional, is_some, is_none, Some, None } from './optional' /** `Result` transpose error */ export const RESULT_TRANSPOSE: Error = new TypeError(`Only Optional can be transpose`) /** `Result` type, `Ok` or `Err` */ export const enum ResultType { /** Ok, the result success type */ Ok, /** Err, the result failure type */ Err } /** * unpack a `Result` transform to `[T, E]` tuple * * @typeparam R result */ export type UnpackResult = R extends Result< infer T, infer E > ? [T, E] : never /** * A container that value either success (Ok) or failure (Err) */ export interface Result { /** * the container value, readonly */ readonly value: T | E /** * the container type, `Ok` or `Err` readonly */ readonly type: ResultType /// PREDICATES /// /** * return `true` if the result is `Ok` */ is_ok(): boolean /** * return `false` if the result is `Err` */ is_err(): boolean /// MAPPING /// /** * map `Result` to `Result` when the result is `Ok`, keep value at `Err` * * @param fn mapper function */ map(fn: (v: T) => U): Result map_async(fn: (v: T) => Promise): Promise> /** * @todo mapOrElse */ // mapOrElse() /** * map `Result` to `Result` when the result is `Err`, keep value at `Ok` * * @param fn mapper function */ map_err(fn: (e: E) => U): Result map_err_async(fn: (e: E) => Promise): Promise> /** * return `Result` when the result is `Ok`, keep value at `Err` * * @param res new result */ and(res: Result): Result /** * call `op` and return `Result` when the result is `Ok`, keep value at `Err` * * @param res new result */ and_then(op: (v: T) => Result): Result and_then_async(op: (v: T) => Promise>): Promise> /** * return `Result` when the result is `Err`, keep value at `Ok` * * @param res new result */ or(res: Result): Result /** * call `op` and return `Result` when the result is `Err`, keep value at `Ok` * * @param res new result */ or_else(op: (e: E) => Result): Result or_else_async(op: (e: E) => Promise>): Promise> /** * unwrap the container, get value, throw when result is `Err` */ unwrap(): T /** * unwrap the container, get value, when the result is `Err`, return given option * * @param u option value */ unwrap_or(u: U): T | U /** * unwrap the container and get value, when the result is `Err`, call op wieh `E` * * @param op caller function */ unwrap_or_else(op: (e: E) => U): T | U unwrap_or_else_async(op: (e: E) => Promise): Promise /** * unwrap and get error, throw when the result is `Ok` */ unwrap_err(): E /** * converts from `Result` to `Optional` */ ok(): Optional /** * converts from `Result` to `Optional` */ err(): Optional /** * transposes a `Result` of an `Optional` into `Optional` of a `Result` * 1. `Ok(Some(v))` will be mapped to `Some(Ok(v))` * 2. `Err(v)` will be mapped to `Some(Err(v))` * 3. `Ok(None)` will be mapped to `None` */ transpose>(): Optional> } class ResultOk implements Result { public readonly type = ResultType.Ok constructor(public readonly value: T) {} map(fn: (v: T) => U): Result { return new ResultOk(fn(this.value)) } async map_async(fn: (v: T) => Promise): Promise> { return new ResultOk(await fn(this.value)) } map_err(_fn: (e: never) => U): Result { return new ResultOk(this.value) } async map_err_async(_fn: (e: never) => Promise): Promise> { return new ResultOk(this.value) } is_ok(): boolean { return true } is_err(): boolean { return false } and(res: Result): Result { return res } and_then(op: (v: T) => Result): Result { return op(this.value) } async and_then_async(op: (v: T) => Promise>): Promise> { return op(this.value) } or(_res: Result): Result { return new ResultOk(this.value) } or_else(_op: (e: never) => Result): Result { return new ResultOk(this.value) } async or_else_async(_op: (e: never) => Promise>): Promise> { return new ResultOk(this.value) } unwrap(): T { return this.value } unwrap_or(_v: any): T { return this.value } unwrap_or_else(_op: (e: never) => U): T { return this.value } async unwrap_or_else_async(_op: (e: never) => Promise): Promise { return this.value } unwrap_err(): never { throw this.value } ok(): Optional { return Some(this.value) } err(): Optional { return None } transpose>(): Optional | never> { if(is_some(this.value)) { return Some(Ok(this.value.unwrap()) ) } else if(is_none(this.value)) { return None } else { throw RESULT_TRANSPOSE } } } class ResultErr implements Result { public readonly type = ResultType.Err constructor(public readonly value: E) {} map(_fn: (v: never) => U): Result { return new ResultErr(this.value) } async map_async(_fn: (v: never) => Promise): Promise> { return new ResultErr(this.value) } map_err(fn: (e: E) => U): Result { return new ResultErr(fn(this.value)) } async map_err_async(fn: (e: E) => Promise): Promise> { return new ResultErr(await fn(this.value)) } is_ok(): boolean { return false } is_err(): boolean { return true } and(_res: Result): Result { return new ResultErr(this.value) } and_then(_op: (v: never) => Result): Result { return new ResultErr(this.value) } async and_then_async(_op: (v: never) => Promise>): Promise> { return new ResultErr(this.value) } or(res: Result): Result { return res } or_else(op: (v: E) => Result): Result { return op(this.value) } async or_else_async(op: (v: E) => Promise>): Promise> { return op(this.value) } unwrap(): never { throw this.value } unwrap_or(v: U): U { return v } unwrap_or_else(op: (e: E) => U): U { return op(this.value) } async unwrap_or_else_async(op: (e: E) => Promise): Promise { return op(this.value) } unwrap_err(): E { return this.value } ok(): Optional { return None } err(): Optional { return Some(this.value) } transpose(): Optional> { return Some(Err(this.value)) } } /** * make a Result Ok container * * @param v input value */ export function Ok(v: T): Result { return new ResultOk(v) } /** * make a Result Err container * * @param v input value */ export function Err(e: E): Result { return new ResultErr(e) } /// PREDICATE /// /** * test value is or not a `Result` * * @param value value */ export function is_result(value: unknown): value is Result { return is_ok(value) || is_err(value) } /** * test value is or not a `Ok` * * @param value value */ export function is_ok(value: unknown): value is Result { return value instanceof ResultOk } /** * test value is or not a `Err` * * @param value value */ export function is_err(value: unknown): value is Result { return value instanceof ResultErr }