Skip to content

Commit

Permalink
refactor: remove unused doc comments
Browse files Browse the repository at this point in the history
  • Loading branch information
yifanwww committed Jun 2, 2024
1 parent 3c520e4 commit cbbb255
Show file tree
Hide file tree
Showing 15 changed files with 284 additions and 1,084 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ module.exports = {
// -------------------- Eslint-Plugin-Jest Rules --------------------

// https://github.com/jest-community/eslint-plugin-jest/blob/main/docs/rules/expect-expect.md
'jest/expect-expect': ['error', { assertFunctionNames: ['expect', '_expect*'] }],
'jest/expect-expect': ['error', { assertFunctionNames: ['expect', 'expect*'] }],

// https://github.com/jest-community/eslint-plugin-jest/blob/main/docs/rules/no-alias-methods.md
'jest/no-alias-methods': 'error',
Expand Down
230 changes: 230 additions & 0 deletions src/Result.class.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
import type { Result } from './Result.type';
import type { Optional, ResultType } from './types.internal';

/**
* The default implementation of interface `Result`.
*/
export class RustlikeResult<T, E> implements Result<T, E> {
private readonly _type: ResultType;
private _value?: T;
private _error?: E;

constructor(type: 'ok', value: T);
constructor(type: 'err', error: E);
constructor(type: ResultType, value: T | E) {
if (type === 'ok') {
this._type = 'ok';
this._value = value as T;
} else {
this._type = 'err';
this._error = value as E;
}
}

/**
* Creates a `Result` that contains the success value.
*/
static Ok<T, E = never>(value: T): Result<T, E> {
return new RustlikeResult<T, E>('ok', value);
}

/**
* Creates a `Result` that contains the error value.
*/
static Err<E, T = never>(error: E): Result<T, E> {
return new RustlikeResult<T, E>('err', error);
}

isOk(): this is Result<T, never> {
return this._type === 'ok';
}

isOkAnd(fn: (value: T) => boolean): boolean {
return this.isOk() && fn(this._value!);
}

async isOkAndAsync(fn: (value: T) => boolean | Promise<boolean>): Promise<boolean> {
return this.isOk() && fn(this._value!);
}

isErr(): this is Result<never, E> {
return this._type === 'err';
}

isErrAnd(fn: (err: E) => boolean): boolean {
return this.isErr() && fn(this._error!);
}

async isErrAndAsync(fn: (err: E) => boolean | Promise<boolean>): Promise<boolean> {
return this.isErr() && fn(this._error!);
}

ok(): Optional<T> {
return this.isOk() ? this._value : undefined;
}

err(): Optional<E> {
return this.isOk() ? undefined : this._error;
}

map<U>(op: (value: T) => U): Result<U, E> {
return this.isOk() ? RustlikeResult.Ok(op(this._value!)) : RustlikeResult.Err(this._error!);
}

async mapAsync<U>(op: (value: T) => U | Promise<U>): Promise<Result<U, E>> {
return this.isOk() ? RustlikeResult.Ok(await op(this._value!)) : RustlikeResult.Err(this._error!);
}

mapOr<U>(fallback: U, map: (value: T) => U): U {
return this.isOk() ? map(this._value!) : fallback;
}

async mapOrAsync<U>(fallback: U, map: (value: T) => U | Promise<U>): Promise<U> {
return this.isOk() ? map(this._value!) : fallback;
}

mapOrElse<U>(fallback: (err: E) => U, map: (value: T) => U): U {
return this.isOk() ? map(this._value!) : fallback(this._error!);
}

async mapOrElseAsync<U>(fallback: (err: E) => U | Promise<U>, map: (value: T) => U | Promise<U>): Promise<U> {
return this.isOk() ? map(this._value!) : fallback(this._error!);
}

mapErr<F>(op: (err: E) => F): Result<T, F> {
return this.isOk() ? RustlikeResult.Ok(this._value!) : RustlikeResult.Err(op(this._error!));
}

async mapErrAsync<F>(op: (err: E) => F | Promise<F>): Promise<Result<T, F>> {
return this.isOk() ? RustlikeResult.Ok(this._value!) : RustlikeResult.Err(await op(this._error!));
}

inspect(fn: (value: T) => void): this {
if (this.isOk()) {
fn(this.unwrapUnchecked());
}
return this;
}

async inspectAsync(fn: (value: T) => void | Promise<void>): Promise<this> {
if (this.isOk()) {
await fn(this.unwrapUnchecked());
}
return this;
}

inspectErr(fn: (err: E) => void): this {
if (this.isErr()) {
fn(this.unwrapErrUnchecked());
}
return this;
}

async inspectErrAsync(fn: (err: E) => void | Promise<void>): Promise<this> {
if (this.isErr()) {
await fn(this.unwrapErrUnchecked());
}
return this;
}

private _unwrapFailed(msg: string, err: unknown): never {
throw new Error(`${msg}: ${String(err)}`);
}

expect(msg: string): T {
return this.isOk() ? this._value! : this._unwrapFailed(msg, this._error!);
}

unwrap(): T {
if (this.isOk()) return this._value!;
throw new Error(String(this._error!));
}

expectErr(msg: string): E {
return this.isErr() ? this._error! : this._unwrapFailed(msg, this._value!);
}

unwrapErr(): E {
if (this.isErr()) return this._error!;
throw new Error(String(this._value!));
}

unwrapOr(fallback: T): T {
return this.isOk() ? this._value! : fallback;
}

unwrapOrElse(op: (err: E) => T): T {
return this.isOk() ? this._value! : op(this._error!);
}

async unwrapOrElseAsync(op: (err: E) => T | Promise<T>): Promise<T> {
return this.isOk() ? this._value! : op(this._error!);
}

// TODO: find a way to do the check in debug/development mode.
unwrapUnchecked(): T {
return this._value!;
}

// TODO: find a way to do the check in debug/development mode.
unwrapErrUnchecked(): E {
return this._error!;
}

and<U>(res: Result<U, E>): Result<U, E> {
return this.isOk() ? res : RustlikeResult.Err(this._error!);
}

andThen<U>(op: (value: T) => Result<U, E>): Result<U, E> {
return this.isOk() ? op(this._value!) : RustlikeResult.Err(this._error!);
}

async andThenAsync<U>(op: (value: T) => Result<U, E> | Promise<Result<U, E>>): Promise<Result<U, E>> {
return this.isOk() ? op(this._value!) : RustlikeResult.Err(this._error!);
}

or<F>(res: Result<T, F>): Result<T, F> {
return this.isOk() ? RustlikeResult.Ok(this._value!) : res;
}

orElse<F>(op: (err: E) => Result<T, F>): Result<T, F> {
return this.isOk() ? RustlikeResult.Ok(this._value!) : op(this._error!);
}

async orElseAsync<F>(op: (err: E) => Result<T, F> | Promise<Result<T, F>>): Promise<Result<T, F>> {
return this.isOk() ? RustlikeResult.Ok(this._value!) : op(this._error!);
}

transpose(): Optional<Result<T & NonNullable<unknown>, E>> {
if (this.isOk()) {
return this._value === undefined || this._value === null ? undefined : RustlikeResult.Ok(this._value);
}
return RustlikeResult.Err(this._error!);
}

private static _equal(self: unknown, other: unknown): boolean {
const isSelfResult = self instanceof RustlikeResult;
const isOtherResult = other instanceof RustlikeResult;

if (isSelfResult && isOtherResult) {
const _self: Result<unknown, unknown> = self;
const _other: Result<unknown, unknown> = other;

const isOk = _self.isOk();
if (isOk !== _other.isOk()) return false;
return isOk
? RustlikeResult._equal(_self.unwrapUnchecked(), _other.unwrapUnchecked())
: RustlikeResult._equal(_self.unwrapErrUnchecked(), _other.unwrapErrUnchecked());
}

return self === other || (Number.isNaN(self) && Number.isNaN(other));
}

equal(other: Result<unknown, unknown>): boolean {
const isOk = this.isOk();
if (isOk !== other.isOk()) return false;
return isOk
? RustlikeResult._equal(this._value, other.unwrapUnchecked())
: RustlikeResult._equal(this._error, other.unwrapErrUnchecked());
}
}
File renamed without changes.
49 changes: 0 additions & 49 deletions src/__tests__/__snapshots__/factory.test.ts.snap

This file was deleted.

17 changes: 0 additions & 17 deletions src/__tests__/__snapshots__/result.test.ts.snap

This file was deleted.

26 changes: 26 additions & 0 deletions src/__tests__/_helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { expect } from '@jest/globals';

import type { RustlikeResult } from '../Result.class';
import type { Result } from '../Result.type';
import type { ResultType } from '../types.internal';

interface ResultTestDescription {
type: ResultType;
value: unknown;
error: unknown;
}

export function expectResult(result: Result<unknown, unknown>, description: ResultTestDescription) {
const _result = result as RustlikeResult<unknown, unknown>;
expect(_result['_type']).toBe(description.type);
if (typeof description.value === 'object' && description.value !== null) {
expect(_result['_value']).toStrictEqual(description.value);
} else {
expect(_result['_value']).toBe(description.value);
}
if (typeof description.error === 'object' && description.error !== null) {
expect(_result['_error']).toStrictEqual(description.error);
} else {
expect(_result['_error']).toBe(description.error);
}
}
18 changes: 10 additions & 8 deletions src/__tests__/factory.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { describe, expect, it } from '@jest/globals';
import { describe, it } from '@jest/globals';

import { Err, Ok } from '../factory';
import type { Result } from '../types';
import type { Result } from '../Result.type';

import { expectResult } from './_helpers';

describe(`Test fn \`${Ok.name}\``, () => {
it('should create `Ok` result', () => {
const result1 = Ok(1);
const result2 = Ok<number, string>(1);
const result3: Result<number, string> = Ok(2);

expect(result1).toMatchSnapshot();
expect(result2).toMatchSnapshot();
expect(result3).toMatchSnapshot();
expectResult(result1, { type: 'ok', value: 1, error: undefined });
expectResult(result2, { type: 'ok', value: 1, error: undefined });
expectResult(result3, { type: 'ok', value: 2, error: undefined });
});
});

Expand All @@ -21,8 +23,8 @@ describe(`Test fn \`${Err.name}\``, () => {
const result2 = Err<number, string>('Some error message');
const result3: Result<number, string> = Err('Some error message');

expect(result1).toMatchSnapshot();
expect(result2).toMatchSnapshot();
expect(result3).toMatchSnapshot();
expectResult(result1, { type: 'err', value: undefined, error: 'Some error message' });
expectResult(result2, { type: 'err', value: undefined, error: 'Some error message' });
expectResult(result3, { type: 'err', value: undefined, error: 'Some error message' });
});
});
Loading

0 comments on commit cbbb255

Please sign in to comment.