Keyborg/shared/utils/resultasync.ts

272 lines
8.0 KiB
TypeScript

import {
Err,
FlattenResult,
Ok,
type Result,
type UnwrapOption,
} from "@shared/utils/result.ts";
import { None, none, Option, Some, some } from "@shared/utils/option.ts";
export class ResultAsync<T, E> implements PromiseLike<Result<T, E>> {
constructor(private readonly _promise: Promise<Result<T, E>>) {
this._promise = _promise;
}
static fromPromise<T, E>(
promise: Promise<T>,
errorMapper: (error: unknown) => E,
) {
const promiseOfResult: Promise<Result<T, E>> = promise
.then((value: T): Result<T, E> => {
return new Ok<T, E>(value);
})
.catch((error: unknown): Result<T, E> => {
return new Err<T, E>(errorMapper(error));
});
return new ResultAsync<T, E>(promiseOfResult);
}
static fromSafePromise<T>(promise: Promise<T>) {
const promiseOfResult: Promise<Result<T, never>> = promise.then(
(value: T): Result<T, never> => {
return new Ok<T, never>(value);
},
);
return new ResultAsync<T, never>(promiseOfResult);
}
static fromThrowable<
Fn extends (...args: readonly any[]) => Promise<any>,
Em extends ErrorMapper<any>,
>(
fn: Fn,
errorMapper?: Em,
): (
...args: Parameters<Fn>
) => ResultAsync<
UnwrapPromise<ReturnType<Fn>>,
ExtractErrorFromMapper<Em>
> {
return (
...args: Parameters<Fn>
): ResultAsync<
UnwrapPromise<ReturnType<Fn>>,
ExtractErrorFromMapper<Em>
> => {
return ResultAsync.fromPromise(
fn(args),
(e) => errorMapper ? errorMapper(e) : e,
);
};
}
async unwrap(): Promise<T> {
const result = await this._promise;
if (result.isErr()) {
throw result.error;
}
return result.value;
}
async match<A, B = A>(
ok: (value: T) => A,
err: (err: E) => B,
): Promise<A | B> {
const result = await this._promise;
if (result.isErr()) {
return err(result.error);
}
return ok(result.value);
}
map<U>(fn: (value: T) => U): ResultAsync<U, E> {
return new ResultAsync(
this._promise.then((result: Result<T, E>): Result<U, E> => {
if (result.isErr()) {
return new Err<U, E>(result.error);
}
return new Ok<U, E>(fn(result.value));
}),
);
}
mapAsync<U>(fn: (value: T) => Promise<U>): ResultAsync<U, E> {
return new ResultAsync(
this._promise.then(
async (result: Result<T, E>): Promise<Result<U, E>> => {
if (result.isErr()) {
return errAsync<U, E>(result.error);
}
return new Ok<U, E>(await fn(result.value));
},
),
);
}
mapErr<U>(fn: (err: E) => U): ResultAsync<T, U> {
return new ResultAsync(
this._promise.then((result: Result<T, E>): Result<T, U> => {
if (result.isErr()) {
return new Err<T, U>(fn(result.error));
}
return new Ok<T, U>(result.value);
}),
);
}
mapErrAsync<U>(fn: (value: T) => Promise<U>): ResultAsync<T, U> {
return new ResultAsync(
this._promise.then(
async (result: Result<T, E>): Promise<Result<T, U>> => {
if (result.isErr()) {
return errAsync<T, U>(await fn(result.error));
}
return new Ok<T, U>(result.value);
},
),
);
}
andThen<U, F>(fn: (value: T) => Result<U, F>): ResultAsync<U, E | F> {
return new ResultAsync(
this._promise.then(
(result: Result<T, E>): ResultAsync<U, E | F> => {
if (result.isErr()) {
return errAsync(result.error);
}
return fn(result.value).toAsync() as ResultAsync<U, E | F>;
},
),
);
}
andThenAsync<U, F>(
fn: (value: T) => ResultAsync<U, E | F> | Promise<Result<U, E | F>>,
): ResultAsync<U, E | F> {
return new ResultAsync(
this._promise.then(
(result: Result<T, E>): ResultAsync<U, E | F> => {
if (result.isErr()) {
return errAsync(result.error);
}
return fn(result.value) as ResultAsync<U, E | F>;
},
),
);
}
nullableToOption(): ResultAsync<Option<NonNullable<T>>, E> {
return this.map((v) => (v ? some(v) : none));
}
flatten(): FlattenResultAsync<ResultAsync<T, E>> {
return new ResultAsync(
this._promise.then(
(result: Result<T, E>): FlattenResult<Result<T, E>> => {
return result.flatten();
},
),
) as FlattenResultAsync<ResultAsync<T, E>>;
}
flattenOption<U = never>(
errFn: () => U,
): ResultAsync<UnwrapOption<T>, E | U> {
return new ResultAsync(
this._promise.then(
(result: Result<T, E>): Result<UnwrapOption<T>, E | U> => {
return result.flattenOption(errFn);
},
),
);
}
flattenOptionOrDefault<D = UnwrapOption<T>>(
defaultValue: D,
): ResultAsync<UnwrapOption<T> | D, E> {
return new ResultAsync(
this._promise.then(
(result: Result<T, E>): Result<UnwrapOption<T> | D, E> => {
return result.flattenOptionOrDefault(defaultValue);
},
),
);
}
matchOption<A, B = A>(
some: (value: UnwrapOption<T>) => A,
none: () => B,
): ResultAsync<A | B, E> {
return new ResultAsync(
this._promise.then((result: Result<T, E>): Result<A | B, E> => {
return result.matchOption(some, none);
}),
);
}
matchOptionAndFlatten<A, B, U, F>(
some: (value: UnwrapOption<T>) => Result<A, U>,
none: () => Result<B, F>,
): ResultAsync<A | B, E | U | F> {
return new ResultAsync(
this._promise.then(
(result: Result<T, E>): Result<A | B, E | U | F> => {
return result.matchOptionAndFlatten(some, none);
},
),
);
}
then<A, B>(
onFulfilled?: (res: Result<T, E>) => A | PromiseLike<A>,
onRejected?: (reason: unknown) => B | PromiseLike<B>,
): PromiseLike<A | B> {
return this._promise.then(onFulfilled, onRejected);
}
}
export function okAsync<T, E = never>(value: T): ResultAsync<T, E>;
export function okAsync<T extends void = void, E = never>(
value: void,
): ResultAsync<void, E>;
export function okAsync<T, E = never>(value: T): ResultAsync<T, E> {
return new ResultAsync(Promise.resolve(new Ok<T, E>(value)));
}
export function errAsync<T = never, E extends string = string>(
err: E,
): ResultAsync<T, E>;
export function errAsync<T = never, E = unknown>(err: E): ResultAsync<T, E>;
export function errAsync<T = never, E extends void = void>(
err: void,
): ResultAsync<T, void>;
export function errAsync<E, T = never>(err: E): ResultAsync<T, E> {
return new ResultAsync(Promise.resolve(new Err<T, E>(err)));
}
type FlattenResultAsync<R> = R extends
ResultAsync<infer Inner, infer OuterError>
? Inner extends ResultAsync<infer T, infer InnerError>
? ResultAsync<T, OuterError | InnerError>
: R
: R;
type UnwrapPromise<Pr extends Promise<unknown>> = Pr extends Promise<infer U>
? U
: never;
type ErrorMapper<E> = ((e: unknown) => E) | undefined;
type ExtractErrorFromMapper<Em extends ErrorMapper<unknown>> = Em extends
(e: unknown) => infer E ? E : unknown;