diff --git a/.npmignore b/.npmignore index f0daeba..60750e8 100644 --- a/.npmignore +++ b/.npmignore @@ -14,4 +14,5 @@ npm-debug.log .github /test -/badges \ No newline at end of file +/badges +/coverage \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b76d989..fe9d5d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## 1.2.0 (October 6, 2021) + +### Release 1.2.0 + +- defining Result interface: IResult +- new feature: **_Error Set_** -> `createErrorSet`, `ErrSet` + +--- + ## 1.1.3 (October 5, 2021) ### Release 1.1.3 diff --git a/LICENSE b/LICENSE index 360273e..2aeddb0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 just-do-halee(=Hwakyeom Kim) +Copyright 2021 just-do-halee(=Hwakyeom Kim) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index d6e8568..07175cd 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,17 @@ # `rusultTs` -Rust **_Result Implementation for Typescript_**, simply. i.e. Modern error handling library. (no dependencies, pure Typescript code about 50 lines) 100% [[coverage]](https://github.com/just-do-halee/rusultts/actions/workflows/main.yml) +Rust **_Result Implementation for Typescript_**, simply. i.e. Modern error handling library. (no dependencies, pure Typescript code about 50 lines) 100% [[coverage]][ci-url]

-![Coverage lines](./badges/badge-lines.svg) -![Coverage functions](./badges/badge-functions.svg) -![Coverage branches](./badges/badge-branches.svg) -![Coverage statements](./badges/badge-statements.svg) +[![Coverage lines](./badges/badge-lines.svg)][ci-url] +[![Coverage functions](./badges/badge-functions.svg)][ci-url] +[![Coverage branches](./badges/badge-branches.svg)][ci-url] +[![Coverage statements](./badges/badge-statements.svg)][ci-url] [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] -[![CI](https://github.com/just-do-halee/rusultts/actions/workflows/main.yml/badge.svg)](https://github.com/just-do-halee/rusultts/actions/workflows/main.yml) +[![CI](https://github.com/just-do-halee/rusultts/actions/workflows/main.yml/badge.svg)][ci-url] [![License][license-image]][license-url] [[changelog]](CHANGELOG.md) @@ -79,6 +79,43 @@ try { } ``` +## **Advanced**
+ +```ts +import { createErrorSet } from 'rusultts'; + +const err = createErrorSet({ + notFound: 'not found', + somethingWrong: 'something wrong...', + wrongHeader: 'please fix your header.', + undefinedValue: 'this value is undefined:', + dividedByZero: 'do not divide by Zero.', + dividedByNegative: 'well, you did divide as Negative value.', +}); +``` + +```ts +import { ResultBox, Ok, Err } from 'rusultts'; +// and also import our **const `err`** + +function divide(a: number, b: number): ResultBox => { + if (b === 0) { + return err.new('dividedByZero', b); // autocompleted string + } else if (b < 0) { + return err.new('dividedByNegative', b); + } + return Ok.new(a / b); +}; + +try { + divide(4, -2).unwrap(); +} catch (e) { + const val1 = err.match(e, 'dividedByZero').unwrap(); // val1 === undefined + const val2 = err.match(e, 'dividedByNegative').unwrap(); // val2 === '-2' + const val3 = err.match({ is: 'not errorType' }, 'dividedByNegative').unwrap(); // throw new Error() +} +``` + ## **License**
[MIT](LICENSE) @@ -89,3 +126,4 @@ try { [downloads-url]: https://npmcharts.com/compare/rusultts?minimal=true [license-url]: https://opensource.org/licenses/MIT [license-image]: https://img.shields.io/npm/l/rusultts +[ci-url]: https://github.com/just-do-halee/rusultts/actions/workflows/main.yml diff --git a/dist/rusultts.d.ts b/dist/rusultts.d.ts index f44cbe6..699c19d 100644 --- a/dist/rusultts.d.ts +++ b/dist/rusultts.d.ts @@ -1,7 +1,18 @@ +/** + * kind of internal subject(ok or err) + */ export declare type ResultObject = { readonly error?: Error; readonly value: T; }; +/** + * international interface + */ +export interface IResult { + readonly isOk: boolean; + readonly isErr: boolean; + unwrap(): T | never; +} /** * ## Examples *```ts @@ -25,7 +36,7 @@ export declare type ResultObject = { * } *``` */ -export declare abstract class ResultBox { +export declare abstract class ResultBox implements IResult { protected readonly val: ResultObject; readonly isOk: boolean; readonly isErr: boolean; @@ -96,3 +107,75 @@ export declare class Err extends ResultBox { * easy one, has value of Error as `null` */ export declare type Result = ResultBox; +/** + * error's message pair object + * ## Example + * ```ts + * const mp: MessagePair = { + * notFound: 'not found', + * somethingWrong: 'something wrong...', + * wrongHeader: 'please fix your header.' + * } + * ``` + */ +export declare type MessagePair = { + [key: string]: string; +}; +export declare type TOrUndefinedToNull = T extends undefined ? null : T; +/** + * creates errors that have already been set. + * ## Example + * ```ts + * const err = createErrorSet({ + * notFound: 'not found', + * somethingWrong: 'something wrong...', + * wrongHeader: 'please fix your header.' + * }); + * + * err.new('wrongHeader'); // === Err.new('please fix your header.', null) + * ``` + */ +export declare class ErrSet { + readonly messagePair: M; + constructor(messagePair: M); + /** + * creates and return the error that have already been set. + */ + new(errorMessageType: keyof M, val: TOrUndefinedToNull): Err>; + /** + * + * @param {Error} e the error in the scope of try~catch. + * @param {MessagePair} errorMessageType in the MessagePair. + * @returns if `e` is not Error type, return Err<, Type>, or returns Ok which means `e` === the error of errorMessageType then returns `error value` or `undefined`. + * + * ## Example + *```ts + * const test = divide(4, 0); + * try { + * test.unwrap(); + * } catch (e) { + * const val = err.match(e, 'dividedByZero').unwrap(); + * if(val) { + * return val; + * } else { + * return 'unexpected error.'; + * } + * } + * ``` + */ + match(e: Error | unknown, errorMessageType: keyof M): ResultBox; +} +/** + * creates errors that have already been set. + * ## Example + * ```ts + * const err = createErrorSet({ + * notFound: 'not found', + * somethingWrong: 'something wrong...', + * wrongHeader: 'please fix your header.' + * }); + * + * err.new('wrongHeader'); // === Err.new('please fix your header.', null) + * ``` + */ +export declare const createErrorSet: (messagePair: M) => ErrSet; diff --git a/dist/rusultts.js b/dist/rusultts.js index 5a71905..fa1141e 100644 --- a/dist/rusultts.js +++ b/dist/rusultts.js @@ -1,5 +1,5 @@ "use strict"; -// rusultTs +// (c) 2021 just-do-halee(=Hwakyeom Kim) var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || @@ -16,7 +16,7 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -exports.Err = exports.Ok = exports.ResultBox = void 0; +exports.createErrorSet = exports.ErrSet = exports.Err = exports.Ok = exports.ResultBox = void 0; /** * ## Examples *```ts @@ -141,3 +141,74 @@ var Err = /** @class */ (function (_super) { return Err; }(ResultBox)); exports.Err = Err; +/** + * creates errors that have already been set. + * ## Example + * ```ts + * const err = createErrorSet({ + * notFound: 'not found', + * somethingWrong: 'something wrong...', + * wrongHeader: 'please fix your header.' + * }); + * + * err.new('wrongHeader'); // === Err.new('please fix your header.', null) + * ``` + */ +var ErrSet = /** @class */ (function () { + function ErrSet(messagePair) { + this.messagePair = messagePair; + } + /** + * creates and return the error that have already been set. + */ + ErrSet.prototype.new = function (errorMessageType, val) { + return Err.new(this.messagePair[errorMessageType], val); + }; + /** + * + * @param {Error} e the error in the scope of try~catch. + * @param {MessagePair} errorMessageType in the MessagePair. + * @returns if `e` is not Error type, return Err<, Type>, or returns Ok which means `e` === the error of errorMessageType then returns `error value` or `undefined`. + * + * ## Example + *```ts + * const test = divide(4, 0); + * try { + * test.unwrap(); + * } catch (e) { + * const val = err.match(e, 'dividedByZero').unwrap(); + * if(val) { + * return val; + * } else { + * return 'unexpected error.'; + * } + * } + * ``` + */ + ErrSet.prototype.match = function (e, errorMessageType) { + if (!(e instanceof Error)) { + return Err.new("e is unknown type:", e); + } + var _a = Err.eSplit(e), message = _a[0], value = _a[1]; + return Ok.new(message === this.messagePair[errorMessageType] ? value : undefined); + }; + return ErrSet; +}()); +exports.ErrSet = ErrSet; +/** + * creates errors that have already been set. + * ## Example + * ```ts + * const err = createErrorSet({ + * notFound: 'not found', + * somethingWrong: 'something wrong...', + * wrongHeader: 'please fix your header.' + * }); + * + * err.new('wrongHeader'); // === Err.new('please fix your header.', null) + * ``` + */ +var createErrorSet = function (messagePair) { + return new ErrSet(messagePair); +}; +exports.createErrorSet = createErrorSet; diff --git a/package.json b/package.json index 2118b8d..b5e433f 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "rusultts", "main": "dist/rusultts.js", "types": "dist/rusultts.d.ts", - "version": "1.1.3", + "version": "1.2.0", "description": "Rust Result Implementation for Typescript, simply. i.e. Modern error handling library.", "author": "just-do-halee ", "license": "MIT", diff --git a/src/rusultts.ts b/src/rusultts.ts index d3b304f..db8ea85 100644 --- a/src/rusultts.ts +++ b/src/rusultts.ts @@ -1,10 +1,23 @@ -// rusultTs +// (c) 2021 just-do-halee(=Hwakyeom Kim) +/** + * kind of internal subject(ok or err) + */ export type ResultObject = { readonly error?: Error; readonly value: T; }; +/** + * international interface + */ +export interface IResult { + readonly isOk: boolean; + readonly isErr: boolean; + // Returning internal value or Throw an error + unwrap(): T | never; +} + /** * ## Examples *```ts @@ -28,7 +41,7 @@ export type ResultObject = { * } *``` */ -export abstract class ResultBox { +export abstract class ResultBox implements IResult { readonly isOk: boolean; readonly isErr: boolean; protected constructor(protected readonly val: ResultObject) { @@ -128,3 +141,96 @@ export class Err extends ResultBox { * easy one, has value of Error as `null` */ export type Result = ResultBox; + +/** + * error's message pair object + * ## Example + * ```ts + * const mp: MessagePair = { + * notFound: 'not found', + * somethingWrong: 'something wrong...', + * wrongHeader: 'please fix your header.' + * } + * ``` + */ +export type MessagePair = { [key: string]: string }; + +export type TOrUndefinedToNull = T extends undefined ? null : T; + +/** + * creates errors that have already been set. + * ## Example + * ```ts + * const err = createErrorSet({ + * notFound: 'not found', + * somethingWrong: 'something wrong...', + * wrongHeader: 'please fix your header.' + * }); + * + * err.new('wrongHeader'); // === Err.new('please fix your header.', null) + * ``` + */ +export class ErrSet { + constructor(public readonly messagePair: M) {} + /** + * creates and return the error that have already been set. + */ + new( + errorMessageType: keyof M, + val: TOrUndefinedToNull + ): Err> { + return Err.new(this.messagePair[errorMessageType], val); + } + /** + * + * @param {Error} e the error in the scope of try~catch. + * @param {MessagePair} errorMessageType in the MessagePair. + * @returns if `e` is not Error type, return Err<, Type>, or returns Ok which means `e` === the error of errorMessageType then returns `error value` or `undefined`. + * + * ## Example + *```ts + * const test = divide(4, 0); + * try { + * test.unwrap(); + * } catch (e) { + * const val = err.match(e, 'dividedByZero').unwrap(); + * if(val) { + * return val; + * } else { + * return 'unexpected error.'; + * } + * } + * ``` + */ + match( + e: Error | unknown, + errorMessageType: keyof M + ): ResultBox { + if (!(e instanceof Error)) { + return Err.new(`e is unknown type:`, e); + } + const [message, value] = Err.eSplit(e); + return Ok.new( + message === this.messagePair[errorMessageType] ? value : undefined + ); + } +} + +/** + * creates errors that have already been set. + * ## Example + * ```ts + * const err = createErrorSet({ + * notFound: 'not found', + * somethingWrong: 'something wrong...', + * wrongHeader: 'please fix your header.' + * }); + * + * err.new('wrongHeader'); // === Err.new('please fix your header.', null) + * ``` + */ +export const createErrorSet = ( + messagePair: M +): ErrSet => { + return new ErrSet(messagePair); +}; diff --git a/test/rusultts.test.ts b/test/rusultts.test.ts index 188445b..d8ce403 100644 --- a/test/rusultts.test.ts +++ b/test/rusultts.test.ts @@ -1,4 +1,11 @@ -import { Result, ResultBox, Ok, Err } from '../src/rusultts'; +import { + Result, + ResultBox, + Ok, + Err, + createErrorSet, + ErrSet, +} from '../src/rusultts'; import { Stack } from './test.types'; describe('make some results', () => { @@ -56,23 +63,38 @@ describe('make some results', () => { }); let divide: (a: number, b: number) => ResultBox; + let err: ErrSet<{ + dividedByZero: 'do not divide by Zero.'; + dividedByNegative: 'well, you did divide as Negative value.'; + }>; beforeEach(() => { + err = createErrorSet({ + dividedByZero: 'do not divide by Zero.', + dividedByNegative: 'well, you did divide as Negative value.', + }); + expect(err.messagePair.dividedByZero).toEqual('do not divide by Zero.'); + expect(err.messagePair.dividedByNegative).toEqual( + 'well, you did divide as Negative value.' + ); + divide = (a: number, b: number): ResultBox => { if (b === 0) { - return Err.new(`b cannot be 0.`, b); + return err.new('dividedByZero', b); + } else if (b < 0) { + return err.new('dividedByNegative', b); } return Ok.new(a / b); }; }); - it('divide-> 4 / 2', () => { + it('divides-> 4 / 2', () => { const test = divide(4, 2); expect(test.isOk).toEqual(true); expect(test.isErr).toEqual(false); expect(test.unwrap()).toEqual(2); }); - it('divide-> 4 / 0', () => { + it('divides-> 4 / 0', () => { const test = divide(4, 0); expect(test.isOk).toEqual(false); expect(test.isErr).toEqual(true); @@ -87,6 +109,19 @@ describe('make some results', () => { } }); + it('divides-> 4 / -2', () => { + const test = divide(4, -2); + try { + test.unwrap(); + } catch (e) { + expect(() => + err.match({ type: 'unknown' }, 'dividedByNegative').unwrap() + ).toThrowError('e is unknown type::--> [object Object]'); + expect(err.match(e, 'dividedByNegative').unwrap()).toEqual('-2'); + expect(err.match(e, 'dividedByZero').unwrap()).toBeUndefined(); + } + }); + it('should be ok after entire testing', () => { // just for fun let stack = Stack.withCapacity(-10);