203 lines
5.5 KiB
TypeScript
203 lines
5.5 KiB
TypeScript
import logger from 'log'
|
|
import {Option} from './option'
|
|
|
|
const log = logger('result')
|
|
|
|
export class Result<T, E = []> {
|
|
public constructor(
|
|
protected readonly _data: T | E,
|
|
private readonly _state: State,
|
|
) {}
|
|
|
|
// Constructors
|
|
static ok<T, E>(data: T) : Result<T, E> {
|
|
log.trace('New Ok')
|
|
return new Result<T, E>(data, State.OK)
|
|
}
|
|
static error<T, E>(err: E) : Result<T, E> {
|
|
log.trace('New Error')
|
|
log.debug(`Error : ${err}`)
|
|
return new Result<T, E>(err, State.ERROR)
|
|
}
|
|
static fromObject<T, E>(res: Result<T, E>) : Result<T, E> {
|
|
// @ts-expect-error
|
|
return new Result(res._data, res._state)
|
|
}
|
|
public as_error() : Result<never, E> {
|
|
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<T> {
|
|
if (this._state === State.OK) {
|
|
return Option.some(this._data as T)
|
|
} else {
|
|
return Option.none()
|
|
}
|
|
}
|
|
public map<U>(f: (data: T) => U) : Result<U, E> {
|
|
if (this._state === State.OK) {
|
|
return Result.ok(f(this._data as T))
|
|
} else {
|
|
return Result.error<never, E>(this._data as E)
|
|
}
|
|
}
|
|
public map_or<U>(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<U>(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<T, E> {
|
|
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<E> {
|
|
if (this._state === State.ERROR) {
|
|
return Option.some(this._data as E)
|
|
} else {
|
|
return Option.none()
|
|
}
|
|
}
|
|
public map_err<F>(f: (err: E) => F) : Result<T, F> {
|
|
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<T, E> {
|
|
if (this._state === State.ERROR) {
|
|
f(this._data as E)
|
|
}
|
|
return this
|
|
}
|
|
|
|
// *****************************************************************************************************************
|
|
// Combine
|
|
public and<U>(res: Result<U, E>) : Result<U, E> {
|
|
if (this._state === State.OK) {
|
|
return res
|
|
} else {
|
|
return Result.error(this._data as E)
|
|
}
|
|
}
|
|
public and_then<U>(f: (data: T) => Result<U, E>) : Result<U, E> {
|
|
if (this._state === State.OK) {
|
|
return f(this._data as T)
|
|
} else {
|
|
return Result.error(this._data as E)
|
|
}
|
|
}
|
|
public or<F>(res: Result<T, F>) : Result<T, F> {
|
|
if (this._state === State.OK) {
|
|
return new Result<T, F>(this._data as T, State.OK)
|
|
} else {
|
|
return res
|
|
}
|
|
}
|
|
public or_else<F>(f: (err: E) => Result<T, F>) : Result<T, F> {
|
|
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
|
|
}
|