Skip to content

Commit

Permalink
feat(result): testing
Browse files Browse the repository at this point in the history
  • Loading branch information
alexsasharegan committed Feb 13, 2018
1 parent c104358 commit 1e80ffe
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 21 deletions.
21 changes: 10 additions & 11 deletions src/option.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ import { is_void, Mapper } from "./utils";
import { OptionVariant, expect_never } from ".";

export type Nullable<T> = T | undefined | null;

export type OptionType<T> = Some<T> | None;

export class Option<T> {
constructor(private readonly option: OptionType<T>) {}

match<U>(matcher: {
public match<U>(matcher: {
[OptionVariant.None](): U;
[OptionVariant.Some](val: T): U;
}): U {
Expand All @@ -26,18 +25,18 @@ export class Option<T> {
}
}

is_some(): this is Some<T> {
public is_some(): this is Some<T> {
return this.match({
some: (_: T) => true,
none: () => false,
});
}

is_none(): this is None {
public is_none(): this is None {
return !this.is_some();
}

expect(err_msg: string): T {
public expect(err_msg: string): T {
return this.match({
some: (x: T) => x,
none: () => {
Expand All @@ -46,7 +45,7 @@ export class Option<T> {
});
}

unwrap(): T {
public unwrap(): T {
return this.match({
some: (x: T) => x,
none: () => {
Expand All @@ -55,35 +54,35 @@ export class Option<T> {
});
}

unwrap_or(def: T): T {
public unwrap_or(def: T): T {
return this.match({
some: (x: T) => x,
none: () => def,
});
}

unwrap_or_else(fn: () => T): T {
public unwrap_or_else(fn: () => T): T {
return this.match({
some: (x: T) => x,
none: () => fn(),
});
}

map<U>(fn: Mapper<T, U>): Option<U> {
public map<U>(fn: Mapper<T, U>): Option<U> {
return this.match({
some: (x: T) => Option.Some(fn(x)),
none: () => Option.None<U>(),
});
}

map_or<U>(def: U, fn: Mapper<T, U>): U {
public map_or<U>(def: U, fn: Mapper<T, U>): U {
return this.match({
some: (x: T) => fn(x),
none: () => def,
});
}

map_or_else<U>(def: () => U, fn: Mapper<T, U>): U {
public map_or_else<U>(def: () => U, fn: Mapper<T, U>): U {
return this.match({
some: (x: T) => fn(x),
none: () => def(),
Expand Down
23 changes: 15 additions & 8 deletions src/result.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export type ResultType<T, E> = Ok<T> | Err<E>;
export class Result<T, E> {
constructor(private readonly result: ResultType<T, E>) {}

match<U>(matcher: {
public match<U>(matcher: {
[ResultVariant.Ok](x: T): U;
[ResultVariant.Err](e: E): U;
}): U {
Expand All @@ -24,52 +24,59 @@ export class Result<T, E> {
}
}

is_ok(): this is Ok<T> {
public is_ok(): this is Ok<T> {
return this.match({
ok: (_: T) => true,
err: (_: E) => false,
});
}

is_err(): this is Err<E> {
public is_err(): this is Err<E> {
return !this.is_ok();
}

ok(): Option<T> {
public ok(): Option<T> {
return this.match({
ok: (x: T) => Some(x),
err: (_: E) => None(),
});
}

err(): Option<E> {
public err(): Option<E> {
return this.match({
ok: (_: T) => None(),
err: (e: E) => Some(e),
});
}

map<U>(op: Mapper<T, U>): Result<U, E> {
public map<U>(op: Mapper<T, U>): Result<U, E> {
return this.match({
ok: (t: T) => Result.Ok(op(t)),
err: (e: E) => Result.Err(e),
});
}

map_err<F>(op: Mapper<E, F>): Result<T, F> {
public map_err<F>(op: Mapper<E, F>): Result<T, F> {
return this.match({
ok: (t: T) => Result.Ok(t),
err: (e: E) => Result.Err(op(e)),
});
}

and<U>(res: Result<U, E>): Result<U, E> {
public and<U>(res: Result<U, E>): Result<U, E> {
return this.match({
ok: (_: T) => res,
err: (e: E) => Result.Err(e),
});
}

public and_then<U>(op: (t: T) => Result<U, E>): Result<U, E> {
return this.match({
ok: (t: T) => op(t),
err: (e: E) => Result.Err(e),
});
}

public static Ok<T>(val: T): Result<T, any> {
return new Result(Ok(val));
}
Expand Down
109 changes: 107 additions & 2 deletions src/result.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Result, Ok, Err } from ".";
import { Result, Ok, Err, Some, None } from ".";

describe("Result.Ok", async () => {
it("should return Result of Ok", async () => {
Expand All @@ -18,7 +18,7 @@ describe("Result.Err", async () => {
});
});

describe("Result.is_ok", async () => {
describe("Result.is_ok && Result.is_err", async () => {
it("with Ok", () => {
let res = Ok(10);
expect(res.is_ok()).toBe(true);
Expand All @@ -31,3 +31,108 @@ describe("Result.is_ok", async () => {
expect(res.is_ok()).toBe(false);
});
});

describe("Result.ok", async () => {
it("should equal Some with Ok", async () => {
expect(Ok(2).ok()).toEqual(Some(2));
});

it("should equal None with Err", async () => {
expect(Err("Nothing here").ok()).toEqual(None());
});
});

describe("Result.err", async () => {
it("should equal None with Ok", async () => {
expect(Ok(2).err()).toEqual(None());
});

it("should equal Some with Err", async () => {
expect(Err("Nothing here").err()).toEqual(Some("Nothing here"));
});
});

describe("Result.map", async () => {
it("should transform with Ok", async () => {
let x = Ok(4);
expect(x.map(x => x * x)).toEqual(Ok(16));
});

it("should not transform with Err", async () => {
let x = Err(4);
expect(x.map(x => x * x)).toEqual(Err(4));
});
});

describe("Result.map_err", async () => {
it("should not transform with Ok", async () => {
let x = Ok(2);
expect(x.map_err(num => Object.prototype.toString.call(num))).toEqual(
Ok(2)
);
});

it("should transform with Err", async () => {
let x = Err(13);
expect(x.map_err(x => x.toString())).toEqual(Err("13"));
expect(x.map_err(x => x.toString())).not.toEqual(Err(13));
});
});

describe("Result.and", async () => {
it("with Ok && Err", async () => {
let x = Ok(2);
let y = Err("late error");
expect(x.and(y)).toEqual(Err("late error"));
});

it("with Err && Ok", async () => {
let x = Err("early error");
let y = Ok("foo");
expect(x.and(y)).toEqual(Err("early error"));
});

it("with Err && Err", async () => {
let x = Err("not a 2");
let y = Err("late error");
expect(x.and(y)).toEqual(Err("not a 2"));
});

it("with Ok && Ok", async () => {
let x = Ok(2);
let y = Ok("different result type");
expect(x.and(y)).toEqual(Ok("different result type"));
});
});

describe("Result.and_then", async () => {
it("should work", async () => {
type NumResultFn = (x: number) => Result<number, number>;
let sq: NumResultFn = x => Ok(x * x);
let err: NumResultFn = x => Err(x);

expect(
Ok(2)
.and_then(sq)
.and_then(sq)
).toEqual(Ok(16));

expect(
Ok(2)
.and_then(sq)
.and_then(err)
).toEqual(Err(4));

expect(
Ok(2)
.and_then(err)
.and_then(sq)
).toEqual(Err(2));

expect(
Err(3)
.and_then(sq)
.and_then(sq)
).toEqual(Err(3));
});
});

0 comments on commit 1e80ffe

Please sign in to comment.