diff --git a/package.json b/package.json index e2c549e..e128c73 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "test": "jest --runInBand" }, "dependencies": { + "decod": "^6.1.6", "fp-ts": "^2.6.7", "tslib": "^2.0.0" }, @@ -27,4 +28,4 @@ "ts-jest": "^26.1.1", "typescript": "^3.9.6" } -} \ No newline at end of file +} diff --git a/src/Failure.ts b/src/Failure.ts new file mode 100644 index 0000000..29c8b8b --- /dev/null +++ b/src/Failure.ts @@ -0,0 +1,89 @@ +import * as decod from 'decod'; +import * as Either from 'fp-ts/lib/Either'; +import * as TaskEither from 'fp-ts/lib/TaskEither'; +import * as Task from 'fp-ts/lib/Task'; +import { pipe } from 'fp-ts/lib/pipeable'; + +const decodeErrorMessage = decod.at('message', decod.string); + +export enum FailureType { + Unexpected = 'FailureType_Unexpected', +} + +export type UnexpectedFailure = Failure; + +export class Failure { + constructor({ + reason, + type, + originalError, + }: { + reason: string; + type: T; + originalError?: Error | unknown; + }) { + this.reason = reason; + this.type = type; + if (originalError instanceof Error) { + this.originalError = originalError; + } + } + + reason: string; + + type: T; + + originalError?: Error; + + toError(): Error { + if (this.originalError) return this.originalError; + return new Error(this.reason); + } + + static builder(type: T) { + return (reason: string, originalError?: Error | unknown) => + new Failure({ + reason, + type, + originalError, + }); + } + + static unexpected = Failure.builder(FailureType.Unexpected); + + static fromUnknownError(originalError: unknown | Error): UnexpectedFailure { + return Failure.unexpected(decodeErrorMessage(originalError), originalError); + } + + static toType(type: T): (failure: Failure) => Failure { + return (failure: Failure) => + new Failure({ + reason: failure.reason, + type, + originalError: failure.originalError, + }); + } + + static toUnexpected = Failure.toType(FailureType.Unexpected); + + private static throw = (failure: Failure) => { + throw failure.toError(); + }; + + static eitherUnsafeGet = (either: Either.Either): A => + pipe(either, Either.getOrElseW(Failure.throw)); + + static taskEitherUnsafeGet = ( + taskEither: TaskEither.TaskEither, + ): Task.Task => + pipe( + taskEither, + TaskEither.getOrElseW((failure) => async () => Failure.throw(failure)), + ); +} + +export type FailureTypes> = F[keyof F]; +export interface FailureTypings> { + types: FailureTypes; + failure: Failure>; +} diff --git a/yarn.lock b/yarn.lock index 14559ca..601bef2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1159,6 +1159,11 @@ decimal.js@^10.2.0: resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.0.tgz#39466113a9e036111d02f82489b5fd6b0b5ed231" integrity sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw== +decod@^6.1.6: + version "6.1.6" + resolved "https://registry.yarnpkg.com/decod/-/decod-6.1.6.tgz#de2b7d2c96fc16e7b429bacfc80dc71dc4191967" + integrity sha512-0NccOCIlq/serE+b38GayxgAiUUjpoC7jNt9PH5Ak7QTOo5GhN1owtqkfNMd3RqhNb61lg/i5BqKSF/5Hb1z3A== + decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"