Skip to content

Commit

Permalink
Implement Rescue All
Browse files Browse the repository at this point in the history
  • Loading branch information
LorisSigrist committed Oct 11, 2023
1 parent 076b009 commit 5797634
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 18 deletions.
2 changes: 1 addition & 1 deletion src/core/compiler/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function compileToDictionary(keyVal, locale) {

new ResultMatcher(parse)
.success(addToDictionary)
.rescue(Error, markKeyInvalid)
.rescueAll(markKeyInvalid)
.call(messageSource, {
shouldParseSkeletons: true,
requiresOtherClause: false,
Expand Down
56 changes: 39 additions & 17 deletions src/core/utils/resultMatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@
*/

/**
* Declaratively define a series of Error-Handling strategies for a given function.
* Think of this as a `match` statement for errors.
*
* Declaratively define what should happen for all the possible outcomes of a function.
* This follows an immutable builder pattern, so each method returns a new instance of the ResultMatcher class.
*
* @template {(...args: any) => any} UnsafeFunc
* @template {(result: ReturnType<UnsafeFunc>) => any} SuccessHandler
* @template {Strategy<any, any>[]} [Strategies=[]]
* @template {((e: unknown) => any) | null} [FallbackHandler=null]
*/
export class ResultMatcher {
/** @type {UnsafeFunc} */
Expand All @@ -29,56 +28,74 @@ export class ResultMatcher {
/** @type {SuccessHandler | null} */
#successHandler = null;

/** @type {FallbackHandler} */
#fallbackHandler;

/**
* @param {UnsafeFunc} func
* @param {Strategies} strategies
* @param {SuccessHandler | null} successHandler
* @param {FallbackHandler} fallbackHandler
*/
constructor(
func,
strategies = /** @type {any} */ ([]),
successHandler = null
successHandler = null,
fallbackHandler = /** @type {any} */ (null)
) {
this.#unsafeFunction = func;
this.#strategies = strategies;
this.#successHandler = successHandler;
this.#fallbackHandler = fallbackHandler;
}

/**
* Defines a strategy for a given error type.
*
* @template Prototype
*
* @template Prototype
* @template StrategyReturnType
*
* @param {{ new (): Prototype;} | { prototype: Prototype; }} prototype - The error type to handle. Thrown things will be compared against this with `instanceof`.
* @param {(instance: Prototype) => StrategyReturnType} handler - Callback to handle the error.
* @returns {ResultMatcher<UnsafeFunc, SuccessHandler, [...Strategies, Strategy<Prototype, StrategyReturnType>]>}
* @returns {ResultMatcher<UnsafeFunc, SuccessHandler, [...Strategies, Strategy<Prototype, StrategyReturnType>], FallbackHandler>}
*/
rescue(prototype, handler) {
const registeredStrategy = { prototype, handler };
return new ResultMatcher(this.#unsafeFunction, [
...this.#strategies,
registeredStrategy,
]);
return new ResultMatcher(
this.#unsafeFunction,
[...this.#strategies, registeredStrategy],
this.#successHandler,
this.#fallbackHandler
);
}

/**
* Handle the happy path
* @template {(e:unknown) => any} Handler
*
* @param {Handler} handler
* @returns {ResultMatcher<UnsafeFunc, SuccessHandler, Strategies, Handler>}
*/
rescueAll(handler) {
return new ResultMatcher(this.#unsafeFunction, this.#strategies, this.#successHandler, handler);
}

/**
* Handle the happy path
*
* @template {SuccessHandler} Handler
* @param {Handler} handler
* @returns {ResultMatcher<UnsafeFunc, Handler, Strategies>}
* @returns {ResultMatcher<UnsafeFunc, Handler, Strategies, FallbackHandler>}
*/
success(handler) {
return new ResultMatcher(this.#unsafeFunction, this.#strategies, handler);
return new ResultMatcher(this.#unsafeFunction, this.#strategies, handler, this.#fallbackHandler);
}

/**
* Calls the unsafe function with the given parameters and handles any errors that may be thrown
* Calls the unsafe function with the given parameters and handles any errors that may be thrown
* according to the registered strategies.
*
*
* @param {Parameters<UnsafeFunc>} params
* @returns { ReturnType<SuccessHandler> | ReturnType<Strategies[number]["handler"]>}
* @returns {FallbackHandler extends null ? (ReturnType<SuccessHandler> | ReturnType<Strategies[number]["handler"]>) : (ReturnType<SuccessHandler> | ReturnType<Strategies[number]["handler"]>) | ReturnType<FallbackHandler> }
*/
call(...params) {
let successResult;
Expand All @@ -91,6 +108,11 @@ export class ResultMatcher {
return strategy.handler(e);
}
}

if (this.#fallbackHandler) {
return this.#fallbackHandler(e);
}

throw e;
}

Expand Down

0 comments on commit 5797634

Please sign in to comment.