Skip to content

Commit

Permalink
feat(transform): add transform operator
Browse files Browse the repository at this point in the history
  • Loading branch information
TomokiMiyauci committed Jul 7, 2023
1 parent a159d31 commit 9bb241b
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 0 deletions.
72 changes: 72 additions & 0 deletions operators/transform.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright © 2023 Tomoki Miyauchi. All rights reserved. MIT license.
// This module is browser compatible.

import { isErr, isOk } from "./query.ts";
import { Ok, type Result } from "../spec.ts";

/** Maps a {@linkcode Result<T, E>} to {@linkcode Result<U, E>} by applying {@linkcode fn} to a contained {@linkcode Ok}, leaving an {@linkcode Err}.
*
* @example
* ```ts
* import { Ok, type Result } from "https://deno.land/x/result_js/spec.ts";
* import { map } from "https://deno.land/x/result_js/operators/transform.ts";
* import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
*
* const result: Result<string, unknown> = Ok("Hello, World!");
* const resultLen = map(result, (v) => v.length);
*
* assertEquals(resultLen, Ok(13));
* ```
*/
export function map<T, E, U>(
result: Result<T, E>,
fn: (value: T) => U,
): Result<U, E> {
if (isOk(result)) return Ok(fn(result.get));

return result;
}

/** Returns the provided {@linkcode defaultValue} (if {@linkcode Err}), or applies {@linkcode fn} to the contained value (if {@linkcode Ok}),
*
* @example
* ```ts
* import { Ok, type Result } from "https://deno.land/x/result_js/spec.ts";
* import { mapOr } from "https://deno.land/x/result_js/operators/transform.ts";
* import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
*
* const result: Result<string, number> = Ok("Hello");
* assertEquals(mapOr(result, 0, ({ length }) => length), 5);
* ```
*/
export function mapOr<T, U>(
result: Result<T, unknown>,
defaultValue: U,
fn: (value: T) => U,
): U {
if (isErr(result)) return defaultValue;

return fn(result.get);
}

/** Maps a {@linkcode Result<T, E>} to {@linkcode U} by applying {@linkcode defaultFn} to a contained {@linkcode Err}, or {@linkcode fn} to a contained {@linkcode Ok}.
*
* @example
* ```ts
* import { Ok, type Result } from "https://deno.land/x/result_js/spec.ts";
* import { mapOrElse } from "https://deno.land/x/result_js/operators/transform.ts";
* import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
*
* const result: Result<string, string> = Ok("Hello");
* assertEquals(mapOrElse(result, () => 2 ** 3, ({ length }) => length), 5);
* ```
*/
export function mapOrElse<T, E, U>(
result: Result<T, E>,
defaultFn: (value: E) => U,
fn: (value: T) => U,
): U {
if (isErr(result)) return defaultFn(result.get);

return fn(result.get);
}
82 changes: 82 additions & 0 deletions operators/transform_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright © 2023 Tomoki Miyauchi. All rights reserved. MIT license.

import { map, mapOr, mapOrElse } from "./transform.ts";
import { Err, Ok } from "../spec.ts";
import {
assertEquals,
assertSpyCallArgs,
assertSpyCalls,
describe,
it,
spy,
} from "../_dev_deps.ts";

describe("map", () => {
it("should call mapper if it is Ok", () => {
const INPUT = "Hello, World!";
const option: Ok<string> = Ok(INPUT);
const fn = spy((v: string) => v.length);
const optionLen = map(option, fn);

assertEquals(optionLen, Ok(13));
assertSpyCalls(fn, 1);
assertSpyCallArgs(fn, 0, [INPUT]);
});

it("should not call mapper function if it is Err", () => {
const option: Err<string> = Err("");
const fn = spy((v: string) => v.length);
const optionLen = map(option, fn);

assertEquals(optionLen, Err(""));
assertSpyCalls(fn, 0);
});
});

describe("mapOr", () => {
it("should call mapper if it is Ok", () => {
const INPUT = "Hello, World!";
const option: Ok<string> = Ok(INPUT);
const fn = spy((v: string) => v.length);
const optionLen = mapOr(option, 0, fn);

assertEquals(optionLen, INPUT.length);
assertSpyCalls(fn, 1);
assertSpyCallArgs(fn, 0, [INPUT]);
});

it("should return default value if it is Err", () => {
const option: Err<string> = Err("");
const fn = spy((v: string) => v.length);
const optionLen = mapOr(option, 0, fn);

assertEquals(optionLen, 0);
assertSpyCalls(fn, 0);
});
});

describe("mapOrElse", () => {
it("should call mapper if it is Ok", () => {
const INPUT = "Hello, World!";
const option: Ok<string> = Ok(INPUT);
const fn = spy((v: string) => v.length);
const defaultFn = spy(() => 0);
const optionLen = mapOrElse(option, defaultFn, fn);

assertEquals(optionLen, INPUT.length);
assertSpyCalls(fn, 1);
assertSpyCallArgs(fn, 0, [INPUT]);
assertSpyCalls(defaultFn, 0);
});

it("should return default value if it is Err", () => {
const option: Err<string> = Err("");
const fn = spy((v: string) => v.length);
const defaultFn = spy(() => 0);
const optionLen = mapOrElse(option, defaultFn, fn);

assertEquals(optionLen, 0);
assertSpyCalls(fn, 0);
assertSpyCalls(defaultFn, 1);
});
});

0 comments on commit 9bb241b

Please sign in to comment.