Skip to content

Commit

Permalink
Add docs (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
bkiac authored Aug 19, 2024
1 parent 8f9b463 commit 83a48d1
Show file tree
Hide file tree
Showing 10 changed files with 717 additions and 38 deletions.
32 changes: 0 additions & 32 deletions README.md

This file was deleted.

5 changes: 5 additions & 0 deletions example/db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface Database {
findGradesByStudentId(id: string): Promise<number[] | undefined>;
}

export const db = {} as Database;
85 changes: 85 additions & 0 deletions example/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import {
Result,
Ok,
Err,
Option,
Some,
AsyncResult,
None,
asyncFn,
Panic,
ErrorWithCause,
} from "../";
import {db} from "./db";

function divide(a: number, b: number): Result<number, Error> {
if (b === 0) {
return Err(new Error("division by zero"));
}
return Ok(a / b);
}

// You do not have to use `namespace` pattern, but I find it useful to group errors and their mappers together.
namespace DatabaseError {
export class Unreachable extends ErrorWithCause<Error> {
readonly tag = "DatabaseError.Unreachable";
}

export class ValidationError extends ErrorWithCause<Error> {
readonly tag = "DatabaseError.ValidationError";
}

export function from(error: Error): DatabaseError {
if (error.message === "validation error") {
return new ValidationError(error.message, {cause: error});
}
if (error.message === "unreachable") {
return new Unreachable(error.message, {cause: error});
}
// Add more error variants here, for now we panic if we encounter an unknown error
throw new Panic("unhandled database error", {cause: error});
}
}
export type DatabaseError = DatabaseError.ValidationError | DatabaseError.Unreachable;

// Chain API example:
function findGradesByStudentId(id: string): AsyncResult<Option<number[]>, DatabaseError> {
return Result.fromPromise(db.findGradesByStudentId(id))
.map((grades) => (grades ? Some(grades) : None))
.mapErr(DatabaseError.from);
}

// Or you can use `asyncFn` to wrap functions that return `Promise<Result<T, E>>` to convert return type to `AsyncResult<T, E>`
// Inferred type is `(studentId: string) => AsyncResult<number, Error>`
const getAverageGrade = asyncFn(async (studentId: string) => {
const grades = await findGradesByStudentId(studentId)
.andThen((maybeGrades) => {
return maybeGrades.match({
Some: (grades) => {
if (grades.length === 0) {
return Err(new Error("grades not found"));
}
return Ok(grades);
},
None: () => {
// Map to error if grades not found
return Err(new Error("grades not found"));
},
});
})
.mapErr(() => {
// Map to generic error from database error, or handle each variant
return new Error("failed to get grades");
});

if (grades.isErr()) {
return Err(grades.unwrapErr());
}

// Safe to unwrap because we checked for error
const value = grades.unwrap();
return divide(
value.reduce((a, b) => a + b, 0),
value.length,
);
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@patina/core",
"version": "2.2.0",
"version": "2.2.1",
"type": "module",
"description": "Type-safe nothing-handling and error-handling library",
"repository": {
Expand Down
Loading

0 comments on commit 83a48d1

Please sign in to comment.