perlica
Version:
> [!WARNING] > 🚧 Work in progress!
289 lines (226 loc) • 8.72 kB
text/typescript
/**
* |-----------------------|-------------------|
* | Rust methods | Perlica |
* |-----------------------|-------------------|
* | and | and |
* | and_then | andThen |
* | as_deref | |
* | as_deref_mut | |
* | as_mut | |
* | as_mut_slice | |
* | as_pin_mut | |
* | as_pin_ref | |
* | as_ref | |
* | as_slice | |
* | cloned | |
* | copied | |
* | expect | expect |
* | filter | filter |
* | flatten | flatten |
* | get_or_insert | |
* | get_or_insert_default | |
* | get_or_insert_with | |
* | insert | |
* | inspect | inspect |
* | is_none | isNone |
* | is_none_or | isNoneOr |
* | is_some | isSome |
* | is_some_and | isSomeAnd |
* | iter | |
* | iter_mut | |
* | map | map |
* | map_or | mapOr |
* | map_or_else | mapOrElse |
* | ok_or | okOr |
* | ok_or_else | okOrElse |
* | or | or |
* | or_else | orElse |
* | replace | |
* | take | |
* | take_if | |
* | transpose | transpose |
* | unwrap | unwrap |
* | unwrap_or | unwrapOr |
* | unwrap_or_default | |
* | unwrap_or_else | unwrapOrElse |
* | unwrap_unchecked | |
* | unzip | |
* | xor | xor |
* | zip | |
* | zip_with | |
* |-----------------------|-------------------|
*/
import { OnceIterator } from "./Iterator";
import { err, isErr, ok, type Result } from "./Result";
export type SomeType<R> = R extends Option<infer T> ? T : any;
export interface OptionTrait<T> {
and<U, T>(this: Option<T>, v: Option<U>): Option<U>;
andThen<U, T>(this: Option<T>, f: (v: T) => Option<U>): Option<U>;
expect<T>(this: Option<T>, msg: string): T;
filter<T>(this: Option<T>, f: (v: T) => boolean): Option<T>;
flatten<T>(this: Option<Option<T>>): Option<T>;
inspect<T>(this: Option<T>, f: (v: T) => void): Option<T>;
isNone<T>(this: Option<T>): boolean;
isNoneOr<T>(this: Option<T>, f: (v: T) => boolean): boolean;
isSome<T>(this: Option<T>): boolean;
isSomeAnd<T>(this: Option<T>, f: (v: T) => boolean): boolean;
map<U, T>(this: Option<T>, f: (v: T) => U): Option<U>;
mapOr<U, T>(this: Option<T>, def: U, f: (v: T) => U): U;
mapOrElse<U, T>(this: Option<T>, d: () => U, f: (v: T) => U): U;
okOr<T, E>(this: Option<T>, v: E): Result<T, E>;
okOrElse<T, E>(this: Option<T>, f: () => E): Result<T, E>;
or<T>(this: Option<T>, v: Option<T>): Option<T>;
orElse<T>(this: Option<T>, f: () => Option<T>): Option<T>;
transpose<T, E>(this: Option<Result<T, E>>): Result<Option<T>, E>;
unwrap: <T>(this: Option<T>) => T;
unwrapOr<T>(this: Option<T>, def: T): T;
unwrapOrElse<T>(this: Option<T>, def: () => T): T;
xor<T>(this: Option<T>, v: Option<T>): Option<T>;
[Symbol.iterator](this: Option<T>): OnceIterator<Option<T>, T>;
}
export const isSome = <T>(r: Option<T>): r is Some<T> => r.tag === "some";
export const isNone = <T>(r: Option<T>): r is None => r.tag === "none";
export interface Some<T> extends OptionTrait<T> {
value: T;
tag: "some";
}
export interface None extends OptionTrait<never> {
tag: "none";
}
export type Option<T> = Some<T> | None;
export const OptionProto = <T>(): OptionTrait<T> => ({
and<U, T>(this: Option<T>, v: Option<U>): Option<U> {
return isSome(this) ? v : none();
},
andThen<T, U>(this: Option<T>, f: (v: T) => Option<U>): Option<U> {
return isSome(this) ? f(this.value) : none();
},
expect<T>(this: Option<T>, msg: string): T {
if (isSome(this)) {
return this.value;
} else {
throw new Error(msg);
}
},
filter<T>(this: Option<T>, f: (v: T) => boolean): Option<T> {
return isNone(this)
? none()
: f(this.value) ? this : none();
},
flatten<T>(this: Option<Option<T>>): Option<T> {
return isSome(this) ? this.value : none();
},
inspect<T>(this: Option<T>, f: (v: T) => void): Option<T> {
if (isSome(this)) {
f(this.value);
return some(this.value);
} else {
return none();
}
},
isNone<T>(this: Option<T>): boolean {
return isNone(this);
},
isNoneOr<T>(this: Option<T>, f: (v: T) => boolean): boolean {
return isNone(this) || f(this.value);
},
isSome<T>(this: Option<T>): boolean {
return isSome(this);
},
isSomeAnd<T>(this: Option<T>, f: (v: T) => boolean): boolean {
return isNone(this) ? false : f(this.value);
},
map<T, U>(this: Option<T>, f: (v: T) => U): Option<U> {
return isSome(this) ? some(f(this.value)) : none();
},
mapOr<U, T>(this: Option<T>, def: U, f: (v: T) => U): U {
return isSome(this) ? f(this.value) : def;
},
mapOrElse<U, T>(this: Option<T>, d: () => U, f: (v: T) => U): U {
return isSome(this) ? f(this.value) : d();
},
okOr<T, E>(this: Option<T>, v: E): Result<T, E> {
return isSome(this) ? ok(this.value) : err(v);
},
okOrElse<T, E>(this: Option<T>, f: () => E): Result<T, E> {
return isSome(this) ? ok(this.value) : err(f());
},
or<T>(this: Option<T>, v: Option<T>): Option<T> {
return isSome(this) ? this : v;
},
orElse<T>(this: Option<T>, f: () => Option<T>): Option<T> {
return isSome(this) ? this : f();
},
transpose<T, E>(this: Option<Result<T, E>>): Result<Option<T>, E> {
return isNone(this)
? ok(none())
: isErr(this.value)
? err(this.value.value)
: ok(some(this.value.value));
},
unwrap<T>(this: Option<T>): T {
if (isSome(this)) {
return this.value;
} else {
throw new Error("called `Option.unwrap()` on a `None` value");
}
},
unwrapOr<T>(this: Option<T>, def: T): T {
return isSome(this) ? this.value : def;
},
unwrapOrElse<T>(this: Option<T>, def: () => T): T {
return isSome(this) ? this.value : def();
},
xor<T>(this: Option<T>, v: Option<T>): Option<T> {
return isSome(this) && isSome(v)
? this
: isNone(this) && isSome(v)
? v
: none();
},
[Symbol.iterator]() {
return new OnceIterator(this);
},
});
export const fromNullable = <T>(v: T): Option<NonNullable<T>> =>
v == null ? none() : some(v as NonNullable<T>);
export const fromResult = <T, E>(v: Result<T, E>): Option<T> => v.ok();
export const tryCatch = <T>(f: () => T): Option<T> => {
try {
return some(f());
} catch (_) {
return none();
}
};
export const tryPromise = async <T>(v: Promise<T>): Promise<Option<T>> =>
v.then(s => some(s)).catch(_ => none());
export const some = <T = never>(v: T): Option<T> => {
const a = Object.create(OptionProto<T>());
a.value = v;
a.tag = "some";
return a;
};
export const none = <T = never>(): Option<T> => {
const a = Object.create(OptionProto<T>());
a.tag = "none";
return a;
};
interface Bind {
<T extends Option<any>, R>(
fn: () => Generator<T, R>,
): Option<R>;
}
export const bind: Bind = fn => {
const iter = fn();
let result = iter.next();
while (true) {
if (result.done) {
return some(result.value);
}
if (isSome(result.value)) {
result = iter.next(result.value.value);
} else {
return result.value;
}
}
};