diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5e611bf --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode +./cov \ No newline at end of file diff --git a/match.ts b/match.ts new file mode 100644 index 0000000..a7c2a41 --- /dev/null +++ b/match.ts @@ -0,0 +1,43 @@ +import { Result } from "./result.ts"; + +interface MatchResult { + ok: (value: Ok) => R; + err: (value: Err) => R; +} + +interface MatchPattern { + [key: string]: (val: V) => R; + [key: number]: (val: V) => R; + _: (val: V) => R; +} + +export function match( + value: V, + matchPattern: Partial>, +): R; + +export function match( + res: Result, + match: MatchResult, +): R; + +export function match( + value: Result | V, + matchPattern: MatchResult | Partial>, +): R { + if (value instanceof Result) { + if (value.isOk()) { + return (matchPattern as MatchResult).ok(value.unwrap()); + } + return (matchPattern as MatchResult).err(value.unwrapErr()); + } + + // deno-fmt-ignore + const func = + (matchPattern as MatchPattern)[value] ?? + (matchPattern as MatchPattern)["_"]; + + if (func) return func(value); + + throw Error('No matching pattern found. Use "_" as a rest pattern'); +} diff --git a/match_test.ts b/match_test.ts new file mode 100644 index 0000000..d485c24 --- /dev/null +++ b/match_test.ts @@ -0,0 +1,50 @@ +import { match } from "./match.ts"; +import { Err, Ok } from "./result.ts"; +import { assertEquals } from "https://deno.land/std@0.100.0/testing/asserts.ts"; + +Deno.test({ + name: "match result", + fn: function () { + const okayValue = match(Ok("ok"), { + ok: (val) => val, + err: () => "WRONG", + }); + + assertEquals(okayValue, "ok"); + const errValue = match(Err("err"), { + ok: () => "WRONG", + err: (val) => val, + }); + + assertEquals(errValue, "err"); + }, +}); + +Deno.test({ + name: "match string", + fn: function () { + const value1 = match("str", { + str: (val) => val, + _: () => "REST", + }); + assertEquals(value1, "str"); + + const value2 = match("not", { + str: (val) => val, + _: () => "REST", + }); + + assertEquals(value2, "REST"); + + try { + match("no rest", { + str: (val) => val, + }); + } catch (e) { + assertEquals( + e.message, + 'No matching pattern found. Use "_" as a rest pattern', + ); + } + }, +}); diff --git a/mod.ts b/mod.ts new file mode 100644 index 0000000..8821777 --- /dev/null +++ b/mod.ts @@ -0,0 +1,4 @@ +export { Err, Ok } from "./result.ts"; +export * from "./match.ts"; + +export type { Result } from "./result.ts"; diff --git a/result.ts b/result.ts new file mode 100644 index 0000000..00a84d3 --- /dev/null +++ b/result.ts @@ -0,0 +1,40 @@ +type ResultTypes = "ok" | "err"; + +export class Result { + #ok: Ok | null = null; + #err: Err | null = null; + #isOkay: boolean; + + constructor(res: "ok", value: Ok); + constructor(res: "err", value: Err); + constructor(res: ResultTypes, value: Ok | Err) { + this.#isOkay = res === "ok"; + this.#isOkay ? this.#ok = value as Ok : this.#err = value as Err; + } + + public unwrap(): Ok { + if (this.#isOkay) return this.#ok!; + throw new Error("Tried to unwrap value when value was err"); + } + + public unwrapErr(): Err { + if (!this.#isOkay) return this.#err!; + throw new Error("Tried to unwrap error when value was ok"); + } + + public isErr(): boolean { + return !this.#isOkay; + } + + public isOk(): boolean { + return this.#isOkay; + } +} + +export function Ok(val: Ok): Result { + return new Result("ok", val); +} + +export function Err(val: Err): Result { + return new Result("err", val); +} diff --git a/result_test.ts b/result_test.ts new file mode 100644 index 0000000..d021097 --- /dev/null +++ b/result_test.ts @@ -0,0 +1,38 @@ +import { Err, Ok } from "./result.ts"; +import { + assertEquals, + assertThrows, +} from "https://deno.land/std@0.100.0/testing/asserts.ts"; + +Deno.test({ + name: "Ok Test", + fn: function () { + const res = Ok("unwrap string"); + + assertEquals(res.unwrap(), "unwrap string"); + assertEquals(res.isOk(), true); + assertEquals(res.isErr(), false); + }, +}); + +Deno.test({ + name: "Err test", + fn: function () { + const res = Err("unwrap string"); + + assertEquals(res.unwrapErr(), "unwrap string"); + assertEquals(res.isErr(), true); + assertEquals(res.isOk(), false); + }, +}); + +Deno.test({ + name: "unwraps", + fn: function () { + const eres = Err(""); + const ores = Ok(""); + + assertThrows(eres.unwrap); + assertThrows(ores.unwrapErr); + }, +});