perlica
Version:
<h1 align="center">Perlica</h1>
552 lines (476 loc) • 17.6 kB
text/typescript
/**
* |----------------------|------------------|
* | Rust methods | Perlica |
* |----------------------|------------------|
* | and | and |
* | and_then | andThen |
* | as_deref | |
* | as_deref_mut | |
* | as_mut | |
* | as_ref | |
* | cloned | |
* | copied | |
* | err | err |
* | expect | expect |
* | expect_err | expectErr |
* | flatten | flatten |
* | inspect | inspect |
* | inspect_err | inspectErr |
* | into_err | |
* | into_ok | |
* | is_err | isErr |
* | is_err_and | isErrAnd |
* | is_ok | isOk |
* | is_ok_and | isOkAnd |
* | iter | |
* | iter_mut | |
* | map | map |
* | map_err | mapErr |
* | map_or | mapOr |
* | map_or_else | mapOrElse |
* | ok | ok |
* | or | or |
* | or_else | orElse |
* | transpose | transpose |
* | unwrap | unwrap |
* | unwrap_err | unwrapErr |
* | unwrap_err_unchecked | |
* | unwrap_or | unwrapOr |
* | unwrap_or_default | |
* | unwrap_or_else | unwrapOrElse |
* | unwrap_unchecked | |
* |----------------------|------------------|
*/
import { OnceIterator } from "./Iterator";
import { isNotNullable } from "./Predicate";
import { isNone, none, some, type Option } from "./Option";
export type OkType<R> = R extends Result<infer T, any> ? T : any;
export type ErrType<R> = R extends Result<any, infer E> ? E : any;
export interface ResultTrait<T, E> {
/**
* Returns `res` if `this` is `Ok`, otherwise returns `Err`.
*
* # Examples
* ```ts
* import { err, ok } from "perlica/Result";
*
* const a = ok(4);
* const b = err("new error");
* expect(a.and(b)).toEqual(err("new error"));
*
* const a = err("old error");
* const b = ok(4);
* expect(a.and(b)).toEqual(err("old error"));
*
* const a = err("old error");
* const b = err("new error");
* expect(a.and(b)).toEqual(err("old error"));
*
* const a = ok(4);
* const b = ok(12);
* expect(a.and(b)).toEqual(ok(12));
* ```
* See also the **[Result.and](https://doc.rust-lang.org/std/result/enum.Result.html#method.and)**
*/
and<U, T, E>(this: Result<T, E>, res: Result<U, E>): Result<U, E>;
/**
* Returns `f` call if `this` is `Ok`, otherwise returns `Err`.
*
* # Examples
* ```ts
* import { err, ok } from "perlica/Result";
*
* const string_to_number = (num: string): Result<number, Error> => {
* const v = Number(num);
* return isNaN(v) ? err(Error("Not a Number")) : ok(v);
* };
*
* expect(ok("4").andThen(string_to_number)).toEqual(ok(4));
* expect(ok("hello").andThen(string_to_number)).toEqual(err(Error("Not a Number")));
* expect(err(Error("old error")).andThen(string_to_number)).toEqual(err(Error("old error")));
* ```
* See also the **[Result.and_then](https://doc.rust-lang.org/std/result/enum.Result.html#method.and_then)**
*/
andThen<U, T, E>(this: Result<T, E>, f: (v: T) => Result<U, E>): Result<U, E>;
err<T, E>(this: Result<T, E>): Option<E>;
/**
* Returns the contained value `T` if `this` is `Ok`, otherwise throw exception `msg`.
*
* ```ts
* import { err, ok } from "perlica/Result";
*
* const a = ok(4);
* const b = err("error");
*
* expect(a.expect("my error message")).toEqual(4);
* expect(() => b.expect("my error message")).toThrowError("my error message");
* ```
* See also the **[Result.expect](https://doc.rust-lang.org/std/result/enum.Result.html#method.expect)**
*/
expect<T, E>(this: Result<T, E>, msg: string): T;
/**
* Returns the contained value `E` if `this` is `Err`, otherwise throw exception `msg`.
*
* ```ts
* import { err, ok } from "perlica/Result";
*
* const a = ok(4);
* const b = err("error");
*
* expect(() => a.expectErr("my error message")).toThrowError("my error message");
* expect(b.expectErr("my error message")).toEqual("error");
* ```
* See also the **[Result.expect_err](https://doc.rust-lang.org/std/result/enum.Result.html#method.expect_err)**
*/
expectErr<T, E>(this: Result<T, E>, msg: string): E;
flatten<T, E>(this: Result<Result<T, E>, E>): Result<T, E>;
inspect: <T, E>(this: Result<T, E>, f: (v: T) => void) => Result<T, E>;
inspectErr<T, E>(this: Result<T, E>, f: (v: E) => void): Result<T, E>;
isErr<T, E>(this: Result<T, E>): boolean;
isErrAnd<T, E>(this: Result<T, E>, f: (v: E) => boolean): boolean;
isOk<T, E>(this: Result<T, E>): boolean;
isOkAnd<T, E>(this: Result<T, E>, f: (v: T) => boolean): boolean;
map<U, T, E>(this: Result<T, E>, f: (v: T) => U): Result<U, E>;
mapErr<U, T, E>(this: Result<T, E>, f: (v: E) => U): Result<T, U>;
mapOr<U, T, E>(this: Result<T, E>, def: U, f: (v: T) => U): U;
mapOrElse<U, T, E>(this: Result<T, E>, def: (v: E) => U, f: (v: T) => U): U;
ok<T, E>(this: Result<T, E>): Option<T>;
or<U, T, E>(this: Result<T, E>, f: (v: E) => U): Result<T, U>;
orElse<U, T, E>(this: Result<T, E>, f: (v: E) => Result<T, U>): Result<T, U>;
transpose<T, E>(this: Result<Option<T>, E>): Option<Result<T, E>>;
/**
* Returns the contained value `Ok`, otherwise throw `Error` exception.
*
* ```ts
* import { err, ok } from "perlica/Result";
*
* expect(ok(4).unwrap()).toEqual(4);
* expect(() => err("error").unwrap()).toThrowError(`called \`Result.unwrap()\` on an \`Err\` value: error`);
* ```
* See also the **[Result.unwrap](https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap)**
*/
unwrap<T, E>(this: Result<T, E>): T;
/**
* Returns the contained value `Err`, otherwise throw `Error` exception.
*
* ```ts
* import { err, ok } from "perlica/Result";
*
* expect(() => ok(4).unwrapErr()).toThrowError(`called \`Result.unwrapErr()\` on an \`Ok\` value: 4`);
* expect(err("error").unwrapErr()).toEqual("error");
* ```
* See also the **[Result.unwrap_err](https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap_err)**
*/
unwrapErr<T, E>(this: Result<T, E>): E;
/**
* Returns the contained value `Ok`, otherwise returns `def`.
*
* ```ts
* import { err, ok } from "perlica/Result";
*
* expect(ok(4).unwrapOr(10)).toEqual(4);
* expect(err("error").unwrapOr(10)).toEqual(10);
* ```
* See also the **[Result.unwrap_or](https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap_or)**
*/
unwrapOr<T, E>(this: Result<T, E>, def: T): T;
/**
* Returns the contained value `Ok`, otherwise `def` call.
*
* ```ts
* import { err, ok } from "perlica/Result";
*
* const len = (s: string) => s.length;
*
* expect(ok(4).unwrapOrElse(len)).toEqual(4);
* expect(err("error").unwrapOrElse(len)).toEqual(5);
* ```
* See also the **[Result.unwrap_or_else](https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap_or_else)**
*/
unwrapOrElse<T, E>(this: Result<T, E>, def: (v: E) => T): T;
[Symbol.iterator](this: Result<T, E>): OnceIterator<Result<T, E>, T>;
}
/**
* Returns `that` if `self` is `Ok`, otherwise returns `self`.
*
* ```ts
* import * as R from "perlica/Result";
*
* const a = R.ok(4);
* const b = R.err("new error");
* expect(R.and(a, b)).toEqual(R.err("new error"));
*
* const a = R.err("old error");
* const b = R.ok(4);
* expect(R.and(a, b)).toEqual(R.err("old error"));
*
* const a = R.err("old error");
* const b = R.err("new error");
* expect(R.and(a, b)).toEqual(R.err("old error"));
*
* const a = R.ok(4);
* const b = R.ok(12);
* expect(R.and(a, b)).toEqual(R.ok(12));
* ```
* See also the **[Result.and](https://doc.rust-lang.org/std/result/enum.Result.html#method.and)**
*/
export const and = <U, T, E>(self: Result<T, E>, that: Result<U, E>): Result<U, E> =>
self.and(that);
/**
* Returns `f` call if `self` is `Ok`, otherwise returns `self`.
*
* ```ts
* import * as R from "perlica/Result";
*
* const string_to_number = (num: string): R.Result<number, Error> => {
* const v = Number(num);
* return isNaN(v) ? R.err(Error("Not a Number")) : R.ok(v);
* };
*
* expect(R.andThen(R.ok("4"), string_to_number)).toEqual(R.ok(4));
* expect(R.andThen(R.ok("hello"), string_to_number)).toEqual(R.err(Error("Not a Number")));
* expect(R.andThen(R.err(Error("old error")), string_to_number)).toEqual(R.err(Error("old error")));
* ```
* See also the **[Result.and_then](https://doc.rust-lang.org/std/result/enum.Result.html#method.and_then)**
*/
export const andThen = <U, T, E>(self: Result<T, E>, f: (v: T) => Result<U, E>): Result<U, E> =>
self.andThen(f);
/**
* Returns the contained value `T` if `self` is `Ok`, otherwise throw exception `msg`.
*
* ```ts
* import * as R from "perlica/Result";
*
* expect(R.expect(R.ok(4), "my error message")).toEqual(4);
* expect(() => R.expect(R.err("error"), "my error message")).toThrowError("my error message");
* ```
* See also the **[Result.expect](https://doc.rust-lang.org/std/result/enum.Result.html#method.expect)**
*/
export const expect = <T, E>(self: Result<T, E>, msg: string): T => self.expect(msg);
/**
* Returns the contained value `E` if `self` is `Err`, otherwise throw exception `msg`.
*
* ```ts
* import * as R from "perlica/Result";
*
* expect(() => R.expectErr(R.ok(4), "my error message")).toThrowError("my error message");
* expect(R.expectErr(R.err("error"), "my error message")).toEqual("error");
* ```
* See also the **[Result.expect_err](https://doc.rust-lang.org/std/result/enum.Result.html#method.expect_err)**
*/
export const expectErr = <T, E>(self: Result<T, E>, msg: string): E => self.expectErr(msg);
export const isOk = <T, E>(r: Result<T, E>): r is Ok<T> => r.tag === "ok";
export const isErr = <T, E>(r: Result<T, E>): r is Err<E> => r.tag === "err";
/**
* Returns the contained value `Ok`, otherwise throw `Error` exception.
*
* ```ts
* import * as R from "perlica/Result";
*
* expect(R.unwrap(R.ok(4))).toEqual(4);
* expect(() => R.unwrap(R.err("error"))).toThrowError(`called \`Result.unwrap()\` on an \`Err\` value: error`);
* ```
* See also the **[Result.unwrap](https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap)**
*/
export const unwrap = <T, E>(self: Result<T, E>): T => self.unwrap();
/**
* Returns the contained value `Err`, otherwise throw `Error` exception.
*
* ```ts
* import * as R from "perlica/Result";
*
* expect(() => R.unwrapErr(R.ok(4))).toThrowError(`called \`Result.unwrapErr()\` on an \`Ok\` value: 4`);
* expect(R.unwrapErr(R.err("error"))).toEqual("error");
* ```
* See also the **[Result.unwrap_err](https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap_err)**
*/
export const unwrapErr = <T, E>(self: Result<T, E>): E => self.unwrapErr();
/**
* Returns the contained value `Ok`, otherwise returns `def`.
*
* ```ts
* import * as R from "perlica/Result";
*
* expect(R.unwrapOr(R.ok(4), 10)).toEqual(4);
* expect(R.unwrapOr(R.err("error"), 10)).toEqual(10);
* ```
* See also the **[Result.unwrap_or](https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap_or)**
*/
export const unwrapOr = <T, E>(self: Result<T, E>, def: T): T => self.unwrapOr(def);
/**
* Returns the contained value `Ok`, otherwise `def` call.
*
* ```ts
* import * as R from "perlica/Result";
*
* const len = (s: string) => s.length;
*
* expect(R.unwrapOrElse(R.ok(4), len)).toEqual(4);
* expect(R.unwrapOrElse(R.err("error"), len)).toEqual(5);
* ```
* See also the **[Result.unwrap_or_else](https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap_or_else)**
*/
export const unwrapOrElse = <T, E>(self: Result<T, E>, def: (v: E) => T): T =>
self.unwrapOrElse(def);
export const fromNullable = <T, E>(v: T, f: () => E): Result<T, E> =>
isNotNullable(v) ? ok(v) : err(f());
export const fromOption = <T, E>(self: Option<T>, f: () => E): Result<T, E> => self.okOr(f());
export const tryCatch = <T, E>(f: () => T): Result<T, E> => {
try {
return ok(f());
} catch (e) {
return err(e as E);
}
};
export const tryPromise = async <T, E>(v: Promise<T>): Promise<Result<T, E>> =>
v.then(s => ok(s)).catch(f => err(f));
export const bind: Bind = fn => {
const iter = fn();
let result = iter.next();
while (true) {
if (result.done) {
return ok(result.value);
}
if (isOk(result.value)) {
result = iter.next(result.value.value);
} else {
return result.value;
}
}
};
export const ok = <T, E = never>(v: T): Result<T, E> => {
const a = Object.create(ResultProto<T, E>());
a.value = v;
a.tag = "ok";
return a;
};
export const err = <E, T = never>(v: E): Result<T, E> => {
const a = Object.create(ResultProto<T, E>());
a.value = v;
a.tag = "err";
return a;
};
export interface Ok<T> extends ResultTrait<T, never> {
tag: "ok";
value: T;
}
export interface Err<E> extends ResultTrait<never, E> {
tag: "err";
value: E;
}
export type Result<T, E> = Ok<T> | Err<E>;
export const ResultProto = <T, E>(): ResultTrait<T, E> => ({
and<U, T, E>(this: Result<T, E>, res: Result<U, E>): Result<U, E> {
return isOk(this) ? res : err(this.value);
},
andThen<T, E, U>(this: Result<T, E>, f: (v: T) => Result<U, E>): Result<U, E> {
return isOk(this) ? f(this.value) : err(this.value);
},
err<T, E>(this: Result<T, E>): Option<E> {
return isErr(this) ? some(this.value) : none();
},
expect<T, E>(this: Result<T, E>, msg: string): T {
if (isOk(this)) {
return this.value;
} else {
throw new Error(msg);
}
},
expectErr<T, E>(this: Result<T, E>, msg: string): E {
if (isErr(this)) {
return this.value;
} else {
throw new Error(msg);
}
},
flatten<T, E>(this: Result<Result<T, E>, E>): Result<T, E> {
return this.andThen(v => v);
},
inspect<T, E>(this: Result<T, E>, f: (v: T) => void): Result<T, E> {
if (isOk(this)) {
f(this.value);
return ok(this.value);
} else {
return err(this.value);
}
},
inspectErr<T, E>(this: Result<T, E>, f: (v: E) => void): Result<T, E> {
if (isErr(this)) {
f(this.value);
return err(this.value);
} else {
return ok(this.value);
}
},
isErr<T, E>(this: Result<T, E>): boolean {
return isErr(this);
},
isErrAnd<T, E>(this: Result<T, E>, f: (v: E) => boolean): boolean {
return isErr(this) && f(this.value);
},
isOk<T, E>(this: Result<T, E>): boolean {
return isOk(this);
},
isOkAnd<T, E>(this: Result<T, E>, f: (v: T) => boolean): boolean {
return isOk(this) && f(this.value);
},
map<T, E, U>(this: Result<T, E>, f: (v: T) => U): Result<U, E> {
return isOk(this) ? ok(f(this.value)) : err(this.value);
},
mapErr<T, E, U>(this: Result<T, E>, f: (v: E) => U): Result<T, U> {
return isErr(this) ? err(f(this.value)) : ok(this.value);
},
mapOr<U, T, E>(this: Result<T, E>, def: U, f: (v: T) => U): U {
return isOk(this) ? f(this.value) : def;
},
mapOrElse<U, T, E>(this: Result<T, E>, def: (v: E) => U, f: (v: T) => U): U {
return isOk(this) ? f(this.value) : def(this.value);
},
ok<T, E>(this: Result<T, E>): Option<T> {
return isOk(this) ? some(this.value) : none();
},
or<U, T, E>(this: Result<T, E>, f: (v: E) => U): Result<T, U> {
return isErr(this) ? err(f(this.value)) : ok(this.value);
},
orElse<T, E, U>(this: Result<T, E>, f: (v: E) => Result<T, U>): Result<T, U> {
return isErr(this) ? f(this.value) : ok(this.value);
},
transpose<T, E>(this: Result<Option<T>, E>): Option<Result<T, E>> {
return isErr(this)
? some(err(this.value))
: isNone(this.value)
? none()
: some(ok(this.value.value));
},
unwrap<T, E>(this: Result<T, E>): T {
if (isOk(this)) {
return this.value;
} else {
throw new Error(`called \`Result.unwrap()\` on an \`Err\` value: ${String(this.value)}`);
}
},
unwrapErr<T, E>(this: Result<T, E>): E {
if (isErr(this)) {
return this.value;
} else {
throw new Error(`called \`Result.unwrapErr()\` on an \`Ok\` value: ${String(this.value)}`);
}
},
unwrapOr<T, E>(this: Result<T, E>, def: T): T {
return isOk(this) ? this.value : def;
},
unwrapOrElse<T, E>(this: Result<T, E>, def: (v: E) => T): T {
return isOk(this) ? this.value : def(this.value);
},
[Symbol.iterator]() {
return new OnceIterator(this);
},
});
interface Bind {
<T extends Result<any, any>, R>(
fn: () => Generator<T, R>,
): Result<
R,
T extends Err<infer E> ? E : never
>;
}