Skip to content

Commit 4000125

Browse files
committed
feat(std/results): added result async
1 parent f50c000 commit 4000125

File tree

4 files changed

+84
-1
lines changed

4 files changed

+84
-1
lines changed

experiments/example/src/index.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
11
import './definition'
22

3+
import { Err, err, fromThrowable } from '@sirutils/std/results'
4+
5+
import { exampleTags } from './tag'
36
import { $sayHi } from './users/say-hi'
47

58
// biome-ignore lint/suspicious/noConsole: Redundant
6-
console.log($sayHi('yui').unwrap())
9+
console.log($sayHi('alice').unwrap())
10+
11+
const a = fromThrowable(
12+
(path: string, data: string) => Bun.write(path, data),
13+
e =>
14+
(e instanceof Err
15+
? (e as Err<never, never, never>).appendCause(exampleTags.get('write'))
16+
: err(exampleTags.get('write'), 'unexpected').appendCause(exampleTags.get('write'))
17+
).appendData(e)
18+
)
719

820
export * from './tag'

experiments/example/src/tag.ts

+1
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ export const exampleTags = Tags.create('experiments/example')
77
// causes
88
.add('get-user')
99
.add('say-hi')
10+
.add('write')

packages/std/src/results/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@ export * from './utils/err'
55
export * from './utils/fn'
66
export * from './utils/try'
77
export * from './utils/tag'
8+
export * from './utils/async'
9+
10+
export * from './tag'
+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import type { BlobType } from '../../shared'
2+
3+
import { Err } from './err'
4+
import { Ok } from './ok'
5+
6+
export class ResultAsync<T, N extends Std.ErrorValues, C extends Std.ErrorValues[] = []>
7+
implements PromiseLike<Std.Result<T, N, C>>
8+
{
9+
private _promise: Promise<Std.Result<T, N, C>>
10+
11+
constructor(res: Promise<Std.Result<T, N, C>>) {
12+
this._promise = res
13+
}
14+
15+
// Makes ResultAsync implement PromiseLike<Result>
16+
// biome-ignore lint/suspicious/noThenProperty: Redundant
17+
then<A, B>(
18+
successCallback?: (res: Std.Result<T, N, C>) => A | PromiseLike<A>,
19+
failureCallback?: (reason: unknown) => B | PromiseLike<B>
20+
): PromiseLike<A | B> {
21+
return this._promise.then(successCallback, failureCallback)
22+
}
23+
24+
async *[Symbol.asyncIterator](): AsyncGenerator<Err<never, N, C>, T> {
25+
const result = await this._promise
26+
27+
if (result.isErr()) {
28+
// @ts-expect-error -- This is structurally equivalent and safe
29+
yield errAsync(result.error)
30+
}
31+
32+
// @ts-expect-error -- This is structurally equivalent and safe
33+
return result.value
34+
}
35+
}
36+
37+
export const okAsync = <T, N extends Std.ErrorValues>(value: T): ResultAsync<T, N, never> =>
38+
new ResultAsync(Promise.resolve(new Ok<T, never, never>(value)))
39+
40+
export const errAsync = <N extends Std.ErrorValues>(
41+
err: N,
42+
message = ''
43+
): ResultAsync<never, N, never> =>
44+
new ResultAsync(Promise.resolve(new Err<never, N, never>(err, message)))
45+
46+
export const fromThrowable = <A extends readonly BlobType[], T, R>(
47+
fn: (...args: A) => Promise<T>,
48+
errorFn: (err: unknown) => R
49+
): ((
50+
...args: A
51+
) => ResultAsync<
52+
T,
53+
Std.InferNameType<Std.UnionsToResult<R>>,
54+
Std.InferCauseType<Std.UnionsToResult<R>>
55+
>) => {
56+
return ((...args: BlobType) => {
57+
return new ResultAsync(
58+
(async () => {
59+
try {
60+
return new Ok(await fn(...args))
61+
} catch (error) {
62+
return errorFn(error) as BlobType
63+
}
64+
})()
65+
)
66+
}) as BlobType
67+
}

0 commit comments

Comments
 (0)