import logger from 'log' import {Option} from './option' const log = logger('result') export class Result { public constructor( protected readonly _data: T | E, private readonly _state: State, ) {} // Constructors static ok(data: T) : Result { log.trace('New Ok') return new Result(data, State.OK) } static error(err: E) : Result { log.trace('New Error') log.debug(`Error : ${err}`) return new Result(err, State.ERROR) } static fromObject(res: Result) : Result { // @ts-expect-error return new Result(res._data, res._state) } public as_error() : Result { return Result.error(this.unwrap_err()) } // Getters public state() : State { return this._state } // TODO : implements Iterable<...> // ***************************************************************************************************************** // Ok public is_ok() : boolean { return this._state === State.OK } public is_ok_and(f: (data: T) => boolean) : boolean { return this._state === State.OK && f(this._data as T) } public expect(msg: string) : T { log.trace('expect') if (this._state === State.ERROR) { log.error('unwrapped an Error') log.error(msg) log.debug(`Error : ${this._data}`) throw msg } return this._data as T } public unwrap() : T { log.trace('unwrap') if (this._state === State.ERROR) { log.error("Unwrapped an Error") log.debug(`Error : ${this._data}`) throw "Unwrapped an Error" } return this._data as T } public ok() : Option { if (this._state === State.OK) { return Option.some(this._data as T) } else { return Option.none() } } public map(f: (data: T) => U) : Result { if (this._state === State.OK) { return Result.ok(f(this._data as T)) } else { return Result.error(this._data as E) } } public map_or(default_: U, f: (data: T) => U) : U { if (this._state === State.OK) { return f(this._data as T) } else { return default_ } } public map_or_else(fe: (err: E) => U, f: (data: T) => U) : U { if (this._state === State.OK) { return f(this._data as T) } else { return fe(this._data as E) } } public inspect(f: (data: T) => void) : Result { if (this._state === State.OK) { f(this._data as T) } return this } public unwrap_or(default_: T) : T { if (this._state === State.OK) { return this._data as T } else { return default_ } } public unwrap_or_else(f: (err: E) => T) : T { if (this._state === State.OK) { return this._data as T } else { return f(this._data as E) } } // ***************************************************************************************************************** // Errors public is_err() : boolean { return this._state === State.ERROR } public is_err_and(f: (err: E) => boolean) : boolean { return this._state === State.ERROR && f(this._data as E) } public expect_err(msg: string) : E { log.trace('expect_err') if (this._state === State.OK) { log.error('expect_err on Ok') log.error(msg) throw msg } return this._data as E } public unwrap_err() : E { log.trace('unwrap_err') if (this._state === State.OK) { log.error('unwrap_err on Ok') throw "Result wasn't in error" } return this._data as E } public err() : Option { if (this._state === State.ERROR) { return Option.some(this._data as E) } else { return Option.none() } } public map_err(f: (err: E) => F) : Result { if (this._state === State.ERROR) { return Result.error(f(this._data as E)) } else { return Result.ok(this._data as T) } } public inspect_err(f: (err: E) => void) : Result { if (this._state === State.ERROR) { f(this._data as E) } return this } // ***************************************************************************************************************** // Combine public and(res: Result) : Result { if (this._state === State.OK) { return res } else { return Result.error(this._data as E) } } public and_then(f: (data: T) => Result) : Result { if (this._state === State.OK) { return f(this._data as T) } else { return Result.error(this._data as E) } } public or(res: Result) : Result { if (this._state === State.OK) { return new Result(this._data as T, State.OK) } else { return res } } public or_else(f: (err: E) => Result) : Result { if (this._state === State.OK) { return Result.ok(this._data as T) } else { return f(this._data as E) } } } export const enum State { OK, ERROR }