Skip to content

Commit 2273998

Browse files
authored
refactor(std)!: re-implement result, fix/update option methods (#7)
* refactor(std): re-implement result * fix(std): fix option methods * refactor(std): rename result methods, add comments * feat(std): add ok-or method for option * fix(std): fix result methods * fix!: remove index entrypoint * fix(std): fix circular dependency * chore(std): add result match
1 parent 4c2070d commit 2273998

10 files changed

Lines changed: 200 additions & 226 deletions

File tree

packages/std/package.json

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@
1414
"bugs": "https://github.com/moeru-ai/std/issues",
1515
"sideEffects": false,
1616
"exports": {
17-
".": "./src/index.ts",
1817
"./async-iterator": "./src/async-iterator/index.ts",
1918
"./base64": "./src/base64/index.ts",
2019
"./merge": "./src/merge/index.ts",
20+
"./option": "./src/option/index.ts",
21+
"./result": "./src/result/index.ts",
2122
"./set-interval": "./src/set-interval/index.ts",
2223
"./sleep": "./src/sleep/index.ts",
2324
"./trampoline": "./src/trampoline/index.ts",
@@ -28,10 +29,6 @@
2829
],
2930
"publishConfig": {
3031
"exports": {
31-
".": {
32-
"types": "./dist/index.d.ts",
33-
"default": "./dist/index.js"
34-
},
3532
"./async-iterator": {
3633
"types": "./dist/async-iterator/index.d.ts",
3734
"default": "./dist/async-iterator/index.js"
@@ -44,6 +41,14 @@
4441
"types": "./dist/merge/index.d.ts",
4542
"default": "./dist/merge/index.js"
4643
},
44+
"./option": {
45+
"types": "./dist/option/index.d.ts",
46+
"default": "./dist/option/index.js"
47+
},
48+
"./result": {
49+
"types": "./dist/result/index.d.ts",
50+
"default": "./dist/result/index.js"
51+
},
4752
"./set-interval": {
4853
"types": "./dist/set-interval/index.d.ts",
4954
"default": "./dist/set-interval/index.js"
@@ -61,9 +66,7 @@
6166
"default": "./dist/with-retry/index.js"
6267
},
6368
"./package.json": "./package.json"
64-
},
65-
"main": "./dist/index.js",
66-
"types": "./dist/index.d.ts"
69+
}
6770
},
6871
"scripts": {
6972
"build": "pkgroll",

packages/std/scripts/update-exports.ts

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,33 +11,20 @@ for (let entry of entries) {
1111

1212
const packageJson = JSON.parse(await readFile('./package.json', 'utf8')) as Record<string, unknown>
1313

14-
packageJson.exports = Object.fromEntries([
15-
['.', './src/index.ts'],
16-
...exports.map(e => [`./${e}`, `./src/${e}/index.ts`]),
17-
])
14+
packageJson.exports = Object.fromEntries(
15+
exports.map(e => [`./${e}`, `./src/${e}/index.ts`]),
16+
)
1817

1918
packageJson.publishConfig = {
2019
exports: Object.fromEntries([
21-
['.', {
22-
types: './dist/index.d.ts',
23-
// eslint-disable-next-line perfectionist/sort-objects
24-
default: './dist/index.js',
25-
}],
2620
...exports.map(e => [`./${e}`, {
2721
types: `./dist/${e}/index.d.ts`,
2822
// eslint-disable-next-line perfectionist/sort-objects
2923
default: `./dist/${e}/index.js`,
3024
}]),
3125
['./package.json', './package.json'],
3226
]),
33-
main: './dist/index.js',
34-
types: './dist/index.d.ts',
3527
} satisfies Record<string, unknown>
3628

3729
await writeFile('./package.json', `${JSON.stringify(packageJson, null, 2)}\n`)
3830
console.log('Updated: ./package.json')
39-
40-
const indexTs = exports.map(e => `export * from './${e}'`).join('\n')
41-
42-
await writeFile('./src/index.ts', `${indexTs}\n`)
43-
console.log('Updated: ./src/index.ts')

packages/std/src/index.ts

Lines changed: 0 additions & 8 deletions
This file was deleted.

packages/std/src/option/core.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export interface None {
2+
__mo: 'none'
3+
}
4+
5+
export type Option<T> = None | Some<T>
6+
7+
export interface Some<T> {
8+
__mo: 'some'
9+
value: T
10+
}
11+
12+
export const none: Option<never> = {
13+
__mo: 'none',
14+
}
15+
16+
export const some = <T>(value: T): Option<T> => ({
17+
__mo: 'some',
18+
value,
19+
})

packages/std/src/option/index.ts

Lines changed: 2 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,2 @@
1-
export interface None {
2-
__mo: 'none'
3-
}
4-
5-
export type Option<T> = None | Some<T>
6-
7-
export interface Some<T> {
8-
__mo: 'some'
9-
value: T
10-
}
11-
12-
export const none: Option<never> = {
13-
__mo: 'none',
14-
}
15-
16-
export const some = <T>(value: T): Option<T> => ({
17-
__mo: 'some',
18-
value,
19-
})
20-
21-
export const isSome = <T>(o: Option<T>): o is Some<T> =>
22-
o.__mo === 'some'
23-
24-
export const isNone = <T>(o: Option<T>): o is None =>
25-
o.__mo === 'none'
26-
27-
export const orElse = <T>(o: Option<T>, fallback: Option<T>): Option<T> =>
28-
isSome(o)
29-
? o
30-
: fallback
31-
32-
export const andThen = <T1, T2>(o: Option<T1>, onSome: (s: Some<T1>) => T2): Option<T2> =>
33-
isSome(o)
34-
? some(onSome(o))
35-
: none
36-
37-
export const andThenAsync = async <T1, T2>(o: Option<T1>, onSome: (s: Some<T1>) => Promise<T2>): Promise<Option<T2>> =>
38-
isSome(o)
39-
? some(await onSome(o))
40-
: o
41-
42-
export const map = <T1, T2>(o: Option<T1>, onSomeValue: (v: T1) => T2): Option<T2> =>
43-
isSome(o)
44-
? some(onSomeValue(o.value))
45-
: o
46-
47-
export const mapAsync = async <T1, T2>(o: Option<T1>, onSomeValue: (v: T1) => Promise<T2>): Promise<Option<T2>> =>
48-
isSome(o)
49-
? some(await onSomeValue(o.value))
50-
: o
51-
52-
export const match = <T1, T2>(o: Option<T1>, onSome: (s: Some<T1>) => T2, onNone: () => T2) =>
53-
isSome(o)
54-
? onSome(o)
55-
: onNone()
56-
57-
/** @experimental */
58-
export const extract = <T>(o: Option<T>): T | undefined =>
59-
isSome(o)
60-
? o.value
61-
: undefined
1+
export * from './core'
2+
export * from './methods'

packages/std/src/option/methods.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import type { Result } from '../result/core'
2+
import type { None, Option, Some } from './core'
3+
4+
import { err, ok } from '../result/core'
5+
import { some } from './core'
6+
7+
export const isSome = <T>(o: Option<T>): o is Some<T> =>
8+
o.__mo === 'some'
9+
10+
export const isNone = <T>(o: Option<T>): o is None =>
11+
o.__mo === 'none'
12+
13+
export const or = <T>(o: Option<T>, fallback: Option<T>): Option<T> =>
14+
isSome(o)
15+
? o
16+
: fallback
17+
18+
export const andThen = <T1, T2>(o: Option<T1>, onSome: (s: Some<T1>) => Option<T2>): Option<T2> =>
19+
isSome(o)
20+
? onSome(o)
21+
: o
22+
23+
// eslint-disable-next-line sonarjs/no-identical-functions
24+
export const andThenAsync = async <T1, T2>(o: Option<T1>, onSome: (s: Some<T1>) => Promise<Option<T2>>): Promise<Option<T2>> =>
25+
isSome(o)
26+
? onSome(o)
27+
: o
28+
29+
export const map = <T1, T2>(o: Option<T1>, onSomeValue: (v: T1) => T2): Option<T2> =>
30+
isSome(o)
31+
? some(onSomeValue(o.value))
32+
: o
33+
34+
export const mapAsync = async <T1, T2>(o: Option<T1>, onSomeValue: (v: T1) => Promise<T2>): Promise<Option<T2>> =>
35+
isSome(o)
36+
? some(await onSomeValue(o.value))
37+
: o
38+
39+
export const match = <T1, T2>(o: Option<T1>, onSome: (s: Some<T1>) => T2, onNone: () => T2): T2 =>
40+
isSome(o)
41+
? onSome(o)
42+
: onNone()
43+
44+
export const okOr = <T, E>(o: Option<T>, error: E): Result<T, E> =>
45+
isSome(o)
46+
? ok(o.value)
47+
: err(error)
48+
49+
/** @experimental */
50+
export const extract = <T>(o: Option<T>): T | undefined =>
51+
isSome(o)
52+
? o.value
53+
: undefined

packages/std/src/result/core.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
export interface Err<E> {
2+
__mr: 'err'
3+
error: E
4+
}
5+
6+
export interface Ok<T> {
7+
__mr: 'ok'
8+
value: T
9+
}
10+
11+
export type Result<T, E> = Err<E> | Ok<T>
12+
13+
export const err = <T, E>(error: E): Result<T, E> => ({
14+
__mr: 'err',
15+
error,
16+
})
17+
18+
export const ok = <T, E>(value: T): Result<T, E> => ({
19+
__mr: 'ok',
20+
value,
21+
})

packages/std/src/result/index.test.ts

Lines changed: 0 additions & 67 deletions
This file was deleted.

packages/std/src/result/index.ts

Lines changed: 2 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,2 @@
1-
export interface Result<out T> {
2-
orDefault: <U extends T>(defaultValue: U) => T
3-
orUndefined: () => T | undefined
4-
orElse: <U extends T>(fn: () => U) => T
5-
unwrap: () => T
6-
unwrapOver: (fn: (error: unknown) => void) => T
7-
expect: (message?: string) => T
8-
expectOver: (message?: string, fn?: (error: unknown) => void) => T
9-
map: <U extends T>(fn: (value: T) => U) => Result<U>
10-
mapErr: <U extends T>(fn: (error: unknown) => U) => Result<U>
11-
}
12-
13-
export const Ok = <T>(value: T): Result<T> => {
14-
return {
15-
orDefault: () => value,
16-
orUndefined: () => value,
17-
orElse: fn => fn(),
18-
unwrap: () => value,
19-
unwrapOver: () => value,
20-
expect: () => value,
21-
expectOver: () => value,
22-
map: (fn) => {
23-
try {
24-
return Ok(fn(value))
25-
}
26-
catch (err: unknown) {
27-
return Err(err instanceof Error ? err : new Error(String(err)))
28-
}
29-
},
30-
mapErr: <U extends T>(_fn: (error: unknown) => U) => {
31-
return Ok(value) as Result<U>
32-
},
33-
}
34-
}
35-
36-
export const Err = <T>(error: unknown): Result<T> => {
37-
return {
38-
orDefault: (defaultValue: T) => defaultValue,
39-
orUndefined: () => undefined,
40-
orElse: fn => fn(),
41-
unwrap: () => {
42-
throw error
43-
},
44-
unwrapOver: (fn) => {
45-
fn(error)
46-
throw error
47-
},
48-
expect: (message?: string) => {
49-
const newError = new Error(message ?? 'Result is empty')
50-
newError.cause = error
51-
throw newError
52-
},
53-
expectOver: (message, fn) => {
54-
fn?.(error)
55-
const newError = new Error(message ?? 'Result is empty')
56-
newError.cause = error
57-
throw newError
58-
},
59-
map: () => {
60-
return Err(error)
61-
},
62-
mapErr: (fn) => {
63-
return Err(fn(error))
64-
},
65-
}
66-
}
1+
export * from './core'
2+
export * from './methods'

0 commit comments

Comments
 (0)