add js-fp3
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import { tap, tapError } from './result.js';
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {string} message
|
||||
* @param {T} r
|
||||
* @returns {T}
|
||||
*/
|
||||
export const logReturn = (message, r) => {
|
||||
console.log(message);
|
||||
return r;
|
||||
};
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {string} src
|
||||
* @param {string} message
|
||||
* @returns {(x: T) => void}
|
||||
*/
|
||||
export const output = (src, message) => (x) => {
|
||||
const value = typeof x === 'object' ? JSON.stringify(x) : x;
|
||||
console.log(`[${src}] ${message} | ${value}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {string} src
|
||||
* @param {(x: T) => string} renderText
|
||||
* @returns {(x: T) => void}
|
||||
*/
|
||||
export const outputWith = (src, renderText) => (x) => {
|
||||
console.log(`[${src}] ${renderText(x)}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* @template E
|
||||
* @param {string} src
|
||||
* @param {E} error
|
||||
*/
|
||||
export const outputError = (src, error) => {
|
||||
const value = typeof error === 'object' ? JSON.stringify(error) : error;
|
||||
console.error(`\x1b[1;31m[${src} ERROR]\x1b[0m ${value}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* @template T, E
|
||||
* @param {string} src
|
||||
* @param {string} message
|
||||
* @returns {(result: import('./result.js').Result<T, E>) => import('./result.js').Result<T, E>}
|
||||
*/
|
||||
export const log = (src, message) => tap(output(src, message));
|
||||
|
||||
/**
|
||||
* @template T, E
|
||||
* @param {string} src
|
||||
* @param {(x: T) => string} renderText
|
||||
* @returns {(result: import('./result.js').Result<T, E>) => import('./result.js').Result<T, E>}
|
||||
*/
|
||||
export const logWith = (src, renderText) => tap(outputWith(src, renderText));
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* @template T, E
|
||||
* @typedef {Object} Result
|
||||
* @property {boolean} isSuccess
|
||||
* @property {boolean} isFailure
|
||||
* @property {T} [value] - Present when isSuccess is true
|
||||
* @property {E} [error] - Present when isFailure is true
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T, E
|
||||
* @param {T} value
|
||||
* @returns {Result<T, E>}
|
||||
*/
|
||||
export const ok = (value) => ({
|
||||
isSuccess: true,
|
||||
isFailure: false,
|
||||
value,
|
||||
});
|
||||
|
||||
/**
|
||||
* @template T, E
|
||||
* @param {E} error
|
||||
* @returns {Result<T, E>}
|
||||
*/
|
||||
export const fail = (error) => ({
|
||||
isSuccess: false,
|
||||
isFailure: true,
|
||||
error,
|
||||
});
|
||||
|
||||
/**
|
||||
* @template T, E
|
||||
* @param {() => T} f
|
||||
* @param {(err: Error) => E} exceptionMapper
|
||||
* @returns {Result<T, E>}
|
||||
*/
|
||||
export const catchResult = (f, exceptionMapper) => {
|
||||
try {
|
||||
return ok(f());
|
||||
} catch (e) {
|
||||
return fail(exceptionMapper(e));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @template TIn, TOut, E
|
||||
* @param {(value: TIn) => Result<TOut, E>} binder
|
||||
* @returns {(result: Result<TIn, E>) => Result<TOut, E>}
|
||||
*/
|
||||
export const bind = (binder) => (result) =>
|
||||
result.isSuccess ? binder(result.value) : fail(result.error);
|
||||
|
||||
/**
|
||||
* @template TIn, TOut, E
|
||||
* @param {(value: TIn) => TOut} mapper
|
||||
* @returns {(result: Result<TIn, E>) => Result<TOut, E>}
|
||||
*/
|
||||
export const map = (mapper) => (result) =>
|
||||
result.isSuccess ? ok(mapper(result.value)) : fail(result.error);
|
||||
|
||||
/**
|
||||
* @template T, E
|
||||
* @param {(value: T) => void} action
|
||||
* @returns {(result: Result<T, E>) => Result<T, E>}
|
||||
*/
|
||||
export const tap = (action) => (result) => {
|
||||
if (result.isSuccess) action(result.value);
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* @template T, E
|
||||
* @param {(error: E) => void} action
|
||||
* @returns {(result: Result<T, E>) => Result<T, E>}
|
||||
*/
|
||||
export const tapError = (action) => (result) => {
|
||||
if (result.isFailure) action(result.error);
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* @template T, EIn, EOut
|
||||
* @param {(error: EIn) => EOut} mapper
|
||||
* @returns {(result: Result<T, EIn>) => Result<T, EOut>}
|
||||
*/
|
||||
export const mapError = (mapper) => (result) =>
|
||||
result.isSuccess ? ok(result.value) : fail(mapper(result.error));
|
||||
|
||||
/**
|
||||
* @template T, E, TResult
|
||||
* @param {(value: T) => TResult} onSuccess
|
||||
* @param {(error: E) => TResult} onFailure
|
||||
* @returns {(result: Result<T, E>) => TResult}
|
||||
*/
|
||||
export const match = (onSuccess, onFailure) => (result) =>
|
||||
result.isSuccess ? onSuccess(result.value) : onFailure(result.error);
|
||||
|
||||
/**
|
||||
* @template T, E
|
||||
* @param {(value: T) => void} onSuccess
|
||||
* @param {(error: E) => void} onFailure
|
||||
* @returns {(result: Result<T, E>) => void}
|
||||
*/
|
||||
export const switch_ = (onSuccess, onFailure) => (result) => {
|
||||
if (result.isSuccess) onSuccess(result.value);
|
||||
else onFailure(result.error);
|
||||
};
|
||||
|
||||
/**
|
||||
* Pipe a result through a series of functions (left-to-right composition).
|
||||
* This allows chaining without extending the prototype.
|
||||
* @template T, E
|
||||
* @param {Result<T, E>} result
|
||||
* @param {...Function} fns
|
||||
* @returns {Result<any, any>}
|
||||
*/
|
||||
export const pipe = (result, ...fns) =>
|
||||
fns.reduce((acc, fn) => fn(acc), result);
|
||||
Reference in New Issue
Block a user