-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
90 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import { Async } from './async'; | ||
import { Result } from './result'; | ||
|
||
/** | ||
* A wrapper around Async<Result<T, E>> to help avoid nesting in a common | ||
* scenario. | ||
*/ | ||
export class AsyncResult<T, E> { | ||
#state; | ||
|
||
private constructor(state: Async<Result<T, E>>) { | ||
this.#state = state; | ||
} | ||
|
||
/** | ||
* Represents a pending value | ||
*/ | ||
static Pending = new AsyncResult<never, never>(Async.Pending); | ||
|
||
/** | ||
* Create a box representing failure | ||
*/ | ||
static Err<const E>(err: E) { | ||
return new AsyncResult<never, E>(Async.Ready(Result.Err(err))); | ||
} | ||
|
||
/** | ||
* Create a box representing success | ||
*/ | ||
static Ok<T>(value: T) { | ||
return new AsyncResult<T, never>(Async.Ready(Result.Ok(value))); | ||
} | ||
|
||
/** | ||
* Combine record of boxes and return either record with their unboxed values | ||
* if all boxes represent a success or the first box representing failure | ||
* otherwise | ||
*/ | ||
static all<T extends Readonly<Record<string, AsyncResult<unknown, unknown>>>>( | ||
xs: T, | ||
): AsyncResult< | ||
{ [K in keyof T]: T[K] extends AsyncResult<infer U, unknown> ? U : never }, | ||
{ | ||
[K in keyof T]: T[K] extends AsyncResult<unknown, infer F> ? F : never; | ||
}[keyof T] | ||
> { | ||
return new AsyncResult( | ||
Async.all( | ||
Object.fromEntries(Object.entries(xs).map(([k, v]) => [k, v.#state])), | ||
).mapReady(Result.all), | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
) as any; | ||
} | ||
|
||
/** | ||
* Unpack the box. Requires explicit handling of pending, success and failure | ||
* states. | ||
* This is used to implement most of the operations | ||
*/ | ||
match<U, F = U, P = U>(arms: { | ||
Ok: (value: T) => U; | ||
Err: (value: E) => F; | ||
Pending: () => P; | ||
}) { | ||
return this.#state.match({ | ||
Pending: arms.Pending, | ||
Ready: result => | ||
result.match({ | ||
Err: arms.Err, | ||
Ok: arms.Ok, | ||
}), | ||
}); | ||
} | ||
|
||
/** | ||
* Apply an arbitrary transformation to a success value inside the box and | ||
* return a new box containing the result of that transformation. | ||
* `f` accepts an existing value. Returned value will be put inside the new | ||
* box representing success. Not called if a box represents either a failure | ||
* or a pending state | ||
*/ | ||
mapOk<U>(f: (value: T) => U) { | ||
return this.match<AsyncResult<U, E>>({ | ||
Pending: () => AsyncResult.Pending, | ||
Err: err => AsyncResult.Err(err), | ||
Ok: value => AsyncResult.Ok(f(value)), | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export { Async } from './async'; | ||
export { AsyncResult } from './asyncResult'; | ||
export { Maybe } from './maybe'; | ||
export { Result } from './result'; |