import identity from '../functions/identity'; import Future from './Future'; const symbol = Symbol.for('盾標準図書館結果'); export abstract class Result { abstract is_ok(): this is OkResult; abstract is_err(): this is ErrResult; abstract unwrap(): T; abstract into(): T | E; abstract map( ok: (value: T) => Ook, err?: (error: E) => Oerr ): Result; abstract map_async( ok: (value: T) => Ook | PromiseLike, err?: (error: E) => Oerr | PromiseLike ): FutureResult; abstract map_err(err: (error: E) => Oerr): Result; abstract map_err_async( err: (error: E) => Oerr | PromiseLike ): FutureResult; abstract toJSON(): { ok: T } | { error: E }; protected abstract [symbol]: T | E; static unwrap(result: T | Result): T { if (result && typeof result === 'object' && symbol in result) { return result.unwrap(); } else { return result as T; } } static from(value: T | Result): Result { try { return Ok(Result.unwrap(value)); } catch (err) { return Err(err); } } static try(cb: () => T): Result { try { return Ok(cb()); } catch (err) { return Err(err); } } abstract or(value: A): T | A; abstract or_else(err: (error: E) => A): T | A; abstract or_else_async( err: (error: E) => A | PromiseLike ): Future; abstract map_or(ok: (value: T) => Ook, value: A): Ook | A; abstract map_or_else( ok: (value: T) => Ook, err: (error: E) => A ): Ook | A; map_or_async( ok: (value: T) => Ook | PromiseLike, value: A | PromiseLike ): Future { return this.map_async(ok).or(value); } map_or_else_async( ok: (value: T) => Ook | PromiseLike, err: (error: E) => A | PromiseLike ): Future { return this.map_async(ok).or_else_async(err); } } export class OkResult extends Result { protected [symbol]: T; constructor(value: T) { super(); this[symbol] = value; } is_ok(): this is OkResult { return true; } is_err(): this is ErrResult { return false; } unwrap(): T { return this[symbol]; } into(): T { return this[symbol]; } map( ok: (value: T) => Ook, _err?: (error: E) => Oerr ): OkResult { return new OkResult(ok(this[symbol])); } map_async( ok: (value: T) => Ook | PromiseLike, _err?: (error: E) => Oerr | PromiseLike ): FutureResult { return new FutureResult(() => ok(this[symbol])); } map_err(_err: (error: E) => Oerr): OkResult { return this as unknown as OkResult; } map_err_async( _err: (error: E) => Oerr | PromiseLike ): FutureResult { return new FutureResult(() => this[symbol]); } toJSON() { return { ok: this[symbol] }; } or() { return this[symbol]; } or_else() { return this[symbol]; } or_else_async() { return new Future(() => this[symbol]); } map_or(ok: (value: T) => Ook): Ook | A { return ok(this[symbol]); } map_or_else(ok: (value: T) => Ook): Ook | A { return ok(this[symbol]); } } export class ErrResult extends Result { protected [symbol]: E; constructor(error: E) { super(); this[symbol] = error; } is_ok(): this is OkResult { return false; } is_err(): this is ErrResult { return true; } unwrap(): never { throw this[symbol]; } into(): E { return this[symbol]; } map( _ok: (value: T) => Ook, err?: (error: E) => Oerr ): Result { if (err) { return new ErrResult(err(this[symbol])); } else { return this as unknown as Result; } } map_async( _ok: (value: T) => Ook | PromiseLike, err?: (error: E) => Oerr | PromiseLike ): FutureResult { return new FutureResult(async () => { if (err) { throw await err(this[symbol]); } else { throw this[symbol]; } }); } map_err(err: (error: E) => Oerr): Result { return this.map(identity, err); } map_err_async( err: (error: E) => Oerr | PromiseLike ): FutureResult { return this.map_async(identity, err); } toJSON() { return { error: this[symbol] }; } or(value: A) { return value; } or_else(err: (error: E) => A) { return err(this[symbol]); } or_else_async(err: (error: E) => A | PromiseLike): Future { return new Future((resolve) => resolve(err(this[symbol]))); } map_or(_ok: (value: T) => Ook, value: A): Ook | A { return value; } map_or_else(_ok: (value: T) => Ook, err: (error: E) => A): Ook | A { return err(this[symbol]); } } export class FutureResult extends Future> { constructor(resolver: () => T | PromiseLike) { super(async (resolve) => { try { resolve(new OkResult(await resolver()) as Result); } catch (error) { resolve(new ErrResult(error as E)); } }); } map_async( ok: (value: T) => Ook | PromiseLike, err?: (error: E) => Oerr | PromiseLike ): FutureResult { return new FutureResult(() => this.then((result) => result.map_async(ok, err).then((result) => result.unwrap()) ) ); } map_err_async( err: (error: E) => Oerr | PromiseLike ): FutureResult { return new FutureResult(() => this.then((result) => result.map_err_async(err).then((result) => result.unwrap()) ) ); } unwrap(): Future { return this.then((result) => result.unwrap()); } or(value: A | PromiseLike): Future { return this.then( (result) => result.or(value), () => value ); } or_else(err: (error: E) => A) { return this.then((result) => result.or_else(err), err); } or_else_async(err: (error: E) => A | PromiseLike) { return this.then((result) => result.or_else(err), err); } map_or( ok: (value: T) => Ook, value: A | PromiseLike ): Future { return this.map_async(ok).or(value); } map_or_else( ok: (value: T) => Ook, err: (error: E) => A ): Future { return this.map_async(ok).or_else(err); } map_or_async( ok: (value: T) => Ook | PromiseLike, value: A | PromiseLike ): Future { return this.map_async(ok).or(value); } map_or_else_async( ok: (value: T) => Ook | PromiseLike, err: (error: E) => A | PromiseLike ): Future { return this.map_async(ok).or_else_async(err); } } export type OkFactory = (value: T) => OkResult; export type ErrFactory = (error: E) => ErrResult; export const Ok = new Proxy(OkResult, { apply(_ok, _this, [value]) { return new OkResult(value); }, }) as unknown as typeof OkResult & OkFactory & (() => OkResult); export const Err = new Proxy(ErrResult, { apply(_err, _this, [error]) { return new ErrResult(error); }, }) as unknown as typeof ErrResult & ErrFactory & (() => ErrResult); export default Result; for (const _ of [Result, FutureResult]) { Object.defineProperties(_, { default: { get: () => Result }, FutureResult: { get: () => FutureResult }, Result: { get: () => Result }, Ok: { get: () => Ok }, Err: { get: () => Err }, }); }