Skip to content

Commit

Permalink
Add group (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
bkiac authored Nov 4, 2023
1 parent d089c75 commit 8b441b3
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 9 deletions.
99 changes: 99 additions & 0 deletions src/helpers/group.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import {test, expect, vi} from "vitest"
import {ResultGroup, createGroup} from "./group"
import {ResultError} from ".."

test("constructor", () => {
const handleError = vi.fn()
const result = createGroup(handleError)
expect(result).toBeInstanceOf(ResultGroup)
// @ts-expect-error
expect(result.handleError).toBe(handleError)
})

class FooError extends ResultError {
readonly tag = "foo"
}

class BarError extends ResultError {
readonly tag = "bar"
}

const m = createGroup(() => new FooError())

test("tryFn", () => {
const result = m.tryFn(() => {
throw new Error("error")
})
expect(result.unwrapErr()).toBeInstanceOf(FooError)
})

test("tryFnWith", () => {
const result = m.tryFnWith(
() => {
throw new Error("error")
},
() => new BarError(),
)
expect(result.unwrapErr()).toBeInstanceOf(BarError)
})

test("tryPromise", async () => {
const result = m.tryPromise(Promise.reject(new Error("error")))
await expect(result.unwrapErr()).resolves.toBeInstanceOf(FooError)
})

test("tryPromiseWith", async () => {
const result = m.tryPromiseWith(Promise.reject(new Error("error")), () => new BarError())
await expect(result.unwrapErr()).resolves.toBeInstanceOf(BarError)
})

test("tryAsyncFn", async () => {
const result = m.tryAsyncFn(async () => {
throw new Error("error")
})
await expect(result.unwrapErr()).resolves.toBeInstanceOf(FooError)
})

test("tryAsyncFnWith", async () => {
const result = m.tryAsyncFnWith(
async () => {
throw new Error("error")
},
() => new BarError(),
)
await expect(result.unwrapErr()).resolves.toBeInstanceOf(BarError)
})

test("guard", () => {
const f = m.guard(() => {
throw new Error("error")
})
expect(f().unwrapErr()).toBeInstanceOf(FooError)
})

test("guardWith", () => {
const f = m.guardWith(
() => {
throw new Error("error")
},
() => new BarError(),
)
expect(f().unwrapErr()).toBeInstanceOf(BarError)
})

test("guardAsync", async () => {
const f = m.guardAsync(async () => {
throw new Error("error")
})
await expect(f().unwrapErr()).resolves.toBeInstanceOf(FooError)
})

test("guardAsyncWith", async () => {
const f = m.guardAsyncWith(
async () => {
throw new Error("error")
},
() => new BarError(),
)
await expect(f().unwrapErr()).resolves.toBeInstanceOf(BarError)
})
73 changes: 73 additions & 0 deletions src/helpers/group.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import {tryAsyncFnWith, tryFnWith, tryPromiseWith} from "./try"
import type {ErrorHandler, ResultError} from "../error/result_error"
import type {Result} from "../result/interface"
import type {PromiseResult} from "../result/promise"

export type ResultGroupErrorHandler<E extends ResultError, F extends ResultError> = (error: E) => F

type Fn = (...args: any[]) => any
type AsyncFn = (...args: any[]) => Promise<any>

export class ResultGroup<E extends ResultError> {
private constructor(private readonly handleError: ErrorHandler<E>) {}

tryFn<T>(f: () => T): Result<T, E> {
return tryFnWith(f, this.handleError)
}

tryFnWith<T, F extends ResultError>(
f: () => T,
h: ResultGroupErrorHandler<E, F>,
): Result<T, F> {
return tryFnWith(f, (error) => h(this.handleError(error)))
}

tryPromise<T>(promise: Promise<T>): PromiseResult<T, E> {
return tryPromiseWith(promise, this.handleError)
}

tryPromiseWith<T, F extends ResultError>(
promise: Promise<T>,
h: ResultGroupErrorHandler<E, F>,
): PromiseResult<T, F> {
return tryPromiseWith(promise, (error) => h(this.handleError(error)))
}

tryAsyncFn<T>(f: () => Promise<T>): PromiseResult<T, E> {
return tryAsyncFnWith(f, this.handleError)
}

tryAsyncFnWith<T, F extends ResultError>(
f: () => Promise<T>,
h: ResultGroupErrorHandler<E, F>,
): PromiseResult<T, F> {
return tryAsyncFnWith(f, (error) => h(this.handleError(error)))
}

guard<T extends Fn>(f: T) {
return (...args: Parameters<T>) => this.tryFn(() => f(...args))
}

guardWith<T extends Fn, F extends ResultError>(f: T, h: ResultGroupErrorHandler<E, F>) {
return (...args: Parameters<T>) => this.tryFnWith(() => f(...args), h)
}

guardAsync<T extends AsyncFn>(f: T) {
return (...args: Parameters<T>) => this.tryAsyncFn(() => f(...args))
}

guardAsyncWith<T extends AsyncFn, F extends ResultError>(
f: T,
h: ResultGroupErrorHandler<E, F>,
) {
return (...args: Parameters<T>) => this.tryAsyncFnWith(() => f(...args), h)
}

static with<E extends ResultError>(handleError: ErrorHandler<E>): ResultGroup<E> {
return new ResultGroup(handleError)
}
}

export function createGroup<E extends ResultError>(handleError: ErrorHandler<E>): ResultGroup<E> {
return ResultGroup.with(handleError)
}
15 changes: 6 additions & 9 deletions src/helpers/guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,20 @@ export function guard<T extends Fn>(f: T) {
}
}

export function guardWith<T extends Fn, E extends ResultError>(f: T, handleError: ErrorHandler<E>) {
export function guardWith<T extends Fn, E extends ResultError>(f: T, h: ErrorHandler<E>) {
return function (...args: Parameters<T>) {
return tryFnWith<ReturnType<T>, E>(() => f(...args), handleError)
return tryFnWith<ReturnType<T>, E>(() => f(...args), h)
}
}

export function guardAsync<T extends AsyncFn>(fn: T) {
export function guardAsync<T extends AsyncFn>(f: T) {
return function (...args: Parameters<T>) {
return tryAsyncFn<Awaited<ReturnType<T>>>(() => fn(...args))
return tryAsyncFn<Awaited<ReturnType<T>>>(() => f(...args))
}
}

export function guardAsyncWith<T extends AsyncFn, E extends ResultError>(
fn: T,
handleError: ErrorHandler<E>,
) {
export function guardAsyncWith<T extends AsyncFn, E extends ResultError>(f: T, h: ErrorHandler<E>) {
return function (...args: Parameters<T>) {
return tryAsyncFnWith<Awaited<ReturnType<T>>, E>(() => fn(...args), handleError)
return tryAsyncFnWith<Awaited<ReturnType<T>>, E>(() => f(...args), h)
}
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from "./error/panic"
export * from "./error/result_error"

export * from "./helpers/fn"
export * from "./helpers/group"
export * from "./helpers/guard"
export * from "./helpers/try"

Expand Down

0 comments on commit 8b441b3

Please sign in to comment.