@tempots/std
Version:
Std library for TypeScript. Natural complement to the Tempo libraries.
489 lines (488 loc) • 18.4 kB
JavaScript
import { AsyncResult as n } from "./async-result.js";
const i = {
/**
* Creates a valid `Validation`.
* @returns A `Validation` that is `Valid`.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
valid: { type: "valid" },
/**
* Creates an invalid `Validation`.
* @param error - The error associated with the invalid value.
* @returns A `Validation` that is `Invalid`.
*/
invalid(e) {
return { type: "invalid", error: e };
},
/**
* Checks if a `Validation` is `Valid`.
* @param r - The `Validation` to check.
* @returns `true` if the `Validation` is `Valid`, otherwise `false`.
*/
isValid(e) {
return e.type === "valid";
},
/**
* Checks if a `Validation` is `Invalid`.
* @param r - The `Validation` to check.
* @returns `true` if the `Validation` is `Invalid`, otherwise `false`.
*/
isInvalid(e) {
return e.type === "invalid";
},
/**
* Maps the value of a `Validation` to a new value.
* @param r - The `Validation` to map.
* @param valid - The mapping function for a valid value.
* @param invalid - The mapping function for an invalid value.
* @returns The mapped value.
*/
match: (e, r, u) => i.isValid(e) ? r() : u(e.error),
/**
* Maps the value of a `Validation` to a new `Validation`.
* @param validation - The `Validation` to map.
* @param value - The value to map.
* @returns A new `Validation` with the mapped value.
*/
toResult: (e, r) => i.match(
e,
() => s.success(r),
(u) => s.failure(u)
),
/**
* Executes side effects based on the state of the validation.
* Unlike `match`, all handlers are optional, allowing you to react only to specific states.
* The `else` handler is called when no specific handler is provided for the current state.
* @param v - The validation.
* @param handlers - An object with optional handlers for each state and an optional `else` fallback.
* @returns The validation that was passed in, allowing for chaining.
* @public
*/
effect: (e, r) => (e.type === "valid" ? r.valid ? r.valid() : r.else?.() : r.invalid ? r.invalid(e.error) : r.else?.(), e),
/**
* Execute a function when the `Validation` is valid.
*
* @param r - The `Validation` to check.
* @param apply - The function to execute when the `Validation` is valid.
* @returns The `Validation` object.
*/
whenValid: (e, r) => (i.isValid(e) && r(), e),
/**
* Execute a function when the `Validation` is invalid.
*
* @param r - The `Validation` to check.
* @param apply - The function to execute when the `Validation` is invalid.
* @returns The `Validation` object.
*/
whenInvalid: (e, r) => (i.isInvalid(e) && r(e.error), e),
/**
* Maps the error of an invalid `Validation` to a new error using the provided function.
* For valid validations, the validation is preserved unchanged.
* @param v - The `Validation` to map the error of.
* @param f - The mapping function to apply to the error.
* @returns A new `Validation` with the mapped error if invalid, otherwise the original valid.
* @public
*/
mapError: (e, r) => e.type === "invalid" ? i.invalid(r(e.error)) : e,
/**
* Maps the error of an invalid `Validation` to a new `Validation` using the provided function.
* This allows recovery from errors by returning a valid validation.
* @param v - The `Validation` to recover from.
* @param f - The recovery function that returns a new `Validation`.
* @returns The result of the recovery function if invalid, otherwise the original valid.
* @public
*/
flatMapError: (e, r) => e.type === "invalid" ? r(e.error) : e,
/**
* Combines two validations. Both must be valid for the result to be valid.
* If both are invalid, errors are combined using the provided function.
* @param v1 - The first validation.
* @param v2 - The second validation.
* @param combineErrors - The function to combine two errors.
* @returns A combined validation.
* @public
*/
combine: (e, r, u) => i.isValid(e) && i.isValid(r) ? i.valid : i.isInvalid(e) && i.isInvalid(r) ? i.invalid(u(e.error, r.error)) : i.isInvalid(e) ? e : r,
/**
* Combines multiple validations into a single validation.
* All must be valid for the result to be valid.
* Returns the first invalid validation if any.
* @param validations - The validations to combine.
* @returns A single validation that is valid only if all inputs are valid.
* @public
*/
all: (e) => {
for (const r of e)
if (i.isInvalid(r))
return r;
return i.valid;
},
/**
* Combines multiple validations, accumulating all errors.
* All must be valid for the result to be valid.
* If any are invalid, all errors are collected into an array.
* @param validations - The validations to combine.
* @returns A validation that is valid only if all inputs are valid, otherwise contains all errors.
* @public
*/
allErrors: (e) => {
const r = [];
for (const u of e)
i.isInvalid(u) && r.push(u.error);
return r.length > 0 ? i.invalid(r) : i.valid;
},
/**
* Compares two validations for equality.
* @param v1 - The first validation.
* @param v2 - The second validation.
* @param errorEquals - Optional custom equality function for errors. Defaults to strict equality.
* @returns `true` if the validations are equal, `false` otherwise.
* @public
*/
equals: (e, r, u = (t, l) => t === l) => e.type === "valid" && r.type === "valid" ? !0 : e.type === "invalid" && r.type === "invalid" ? u(e.error, r.error) : !1,
/**
* Recovers from an invalid validation by returning a valid validation.
* @param v - The `Validation` to recover from.
* @returns A valid validation regardless of the input.
* @public
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
recover: (e) => i.valid,
/**
* Gets the error if the validation is invalid, otherwise returns undefined.
* @param v - The validation to get the error from.
* @returns The error if invalid, otherwise undefined.
* @public
*/
getError: (e) => {
if (i.isInvalid(e))
return e.error;
},
/**
* Gets the error if the validation is invalid, otherwise returns the default value.
* @param v - The validation to get the error from.
* @param defaultError - The default error to return if valid.
* @returns The error if invalid, otherwise the default error.
* @public
*/
getErrorOrElse: (e, r) => i.isInvalid(e) ? e.error : r
}, s = {
/**
* Creates a successful `Result`.
* @param value - The value to wrap in a `Success` type.
* @returns A `Result` that is a `Success`.
* @public
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
success(e) {
return { type: "Success", value: e };
},
/**
* Creates a failure `Result`.
* @param error - The error to wrap in a `Failure` type.
* @returns A `Result` that is a `Failure`.
* @public
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
failure(e) {
return { type: "Failure", error: e };
},
/**
* Maps the value of a `Result` to a new value.
* @param r - The `Result` to map.
* @param f - The mapping function.
* @returns A new `Result` with the mapped value.
* @public
*/
map: (e, r) => e.type === "Success" ? s.success(r(e.value)) : e,
/**
* Maps the value of a `Result` to a new `Result`.
* @param r - The `Result` to map.
* @param f - The mapping function.
* @returns A new `Result` with the mapped value.
* @public
*/
flatMap: (e, r) => e.type === "Success" ? r(e.value) : e,
/**
* Converts a `Result` to an `AsyncResult`.
* @param r - The `Result` to convert.
* @returns An `AsyncResult` that is equivalent to the input `Result`.
* @public
*/
toAsync(e) {
return s.match(
e,
(r) => n.success(r),
(r) => n.failure(r)
);
},
/**
* Converts a `Result` to a `Validation`.
* @param r - The `Result` to convert.
* @returns A `Validation` that is equivalent to the input `Result`.
* @public
*/
toValidation(e) {
return s.match(
e,
() => i.valid,
(r) => i.invalid(r)
);
},
/**
* Checks if a `Result` is a success.
* @param r - The `Result` to check.
* @returns `true` if the `Result` is a `Success`, `false` otherwise.
* @public
*/
isSuccess(e) {
return e.type === "Success";
},
/**
* Checks if a `Result` is a failure.
* @param r - The `Result` to check.
* @returns `true` if the `Result` is a `Failure`, `false` otherwise.
* @public
*/
isFailure(e) {
return e.type === "Failure";
},
/**
* Gets the value of a `Result` if it is a `Success`, otherwise returns the provided default value.
* @param r - The `Result` to get the value from.
* @param alt - The default value to return if the `Result` is a `Failure`.
* @returns The value of the `Result` if it is a `Success`, otherwise the default value.
* @public
*/
getOrElse(e, r) {
return s.isSuccess(e) ? e.value : r;
},
/**
* Gets the value of a `Result` if it is a `Success`, otherwise returns the result of the provided function.
* @param r - The `Result` to get the value from.
* @param altf - The function to call if the `Result` is a `Failure`.
* @returns The value of the `Result` if it is a `Success`, otherwise the result of the function.
* @public
*/
getOrElseLazy(e, r) {
return s.isSuccess(e) ? e.value : r();
},
/**
* Gets the value of a `Result` if it is a `Success`, otherwise returns `null`.
* @param r - The `Result` to get the value from.
* @returns The value of the `Result` if it is a `Success`, otherwise `null`.
* @public
*/
getOrNull(e) {
return s.isSuccess(e) ? e.value : null;
},
/**
* Gets the value of a `Result` if it is a `Success`, otherwise returns `undefined`.
* @param r - The `Result` to get the value from.
* @returns The value of the `Result` if it is a `Success`, otherwise `undefined`.
* @public
*/
getOrUndefined(e) {
return s.isSuccess(e) ? e.value : void 0;
},
/**
* Gets the value of a `Result` if it is a `Success`, otherwise it throws the error contained in the `Failure`.
* @param r - The `Result` to get the value from.
* @returns The value of the `Result` if it is a `Success`.
*/
getUnsafe: (e) => {
if (s.isSuccess(e))
return e.value;
throw e.error;
},
/**
* Based on the state of the result, it picks the appropriate function to call and returns the result.
* @param success - The function to call if the result is a success.
* @param failure - The function to call if the result is a failure.
* @returns The result of calling the appropriate function based on the state of the result.
* @public
*/
match: (e, r, u) => s.isSuccess(e) ? r(e.value) : u(e.error),
/**
* Executes side effects based on the state of the result.
* Unlike `match`, all handlers are optional, allowing you to react only to specific states.
* The `else` handler is called when no specific handler is provided for the current state.
* @param r - The result.
* @param handlers - An object with optional handlers for each state and an optional `else` fallback.
* @returns The result that was passed in, allowing for chaining.
* @public
*/
effect: (e, r) => (e.type === "Success" ? r.success ? r.success(e.value) : r.else?.() : r.failure ? r.failure(e.error) : r.else?.(), e),
/**
* Calls the provided function if the result is a success.
* @param apply - The function to call if the result is a success.
* @returns A function that takes a `Result` and calls the provided function if the result is a success.
* @public
*/
whenSuccess: (e, r) => (s.isSuccess(e) && r(e.value), e),
/**
* Calls the provided function if the result is a failure.
* @param apply - The function to call if the result is a failure.
* @returns The result that was passed in.
* @public
*/
whenFailure: (e, r) => (s.isFailure(e) && r(e.error), e),
/**
* Combines two results into a single result.
* @param r1 - The first result.
* @param r2 - The second result.
* @param combineV - The function to combine two values.
* @param combineE - The function to combine two errors.
* @returns The combined result.
* @public
*/
combine: (e, r, u, t) => s.match(
e,
(l) => s.match(
r,
(a) => s.success(u(l, a)),
(a) => s.failure(a)
),
(l) => s.match(
r,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
(a) => s.failure(l),
(a) => s.failure(t(l, a))
)
),
/**
* Compares two results for equality.
* @param r1 - The first result.
* @param r2 - The second result.
* @param options - The options to use for comparison. By default, uses strict equality.
* @returns `true` if the results are equal, `false` otherwise.
*/
equals: (e, r, u = {
valueEquals: (t, l) => t === l,
errorEquals: (t, l) => t === l
}) => e.type === "Success" && r.type === "Success" ? u.valueEquals(e.value, r.value) : e.type === "Failure" && r.type === "Failure" ? u.errorEquals(e.error, r.error) : !1,
/**
* Combines multiple results into a single result.
* @param results - The results to combine.
* @returns A single result that is a success if all the input results are successes, otherwise a failure.
*/
all: (e) => {
const r = [];
for (const u of e)
if (s.isSuccess(u))
r.push(u.value);
else
return u;
return s.success(r);
},
/**
* Maps the error of a failed `Result` to a new error using the provided function.
* For success results, the result is preserved unchanged.
* @param r - The `Result` to map the error of.
* @param f - The mapping function to apply to the error.
* @returns A new `Result` with the mapped error if failed, otherwise the original success.
* @public
*/
mapError: (e, r) => e.type === "Failure" ? s.failure(r(e.error)) : e,
/**
* Maps the error of a failed `Result` to a new `Result` using the provided function.
* This allows recovery from errors by returning a new successful result.
* @param r - The `Result` to recover from.
* @param f - The recovery function that returns a new `Result`.
* @returns The result of the recovery function if failed, otherwise the original success.
* @public
*/
flatMapError: (e, r) => e.type === "Failure" ? r(e.error) : e,
/**
* Recovers from a failure by providing an alternative value.
* @param r - The `Result` to recover from.
* @param f - The function that provides an alternative value given the error.
* @returns A successful `Result` with the alternative value if failed, otherwise the original success.
* @public
*/
recover: (e, r) => e.type === "Failure" ? { type: "Success", value: r(e.error) } : e,
/**
* Applies a function wrapped in a `Result` to a value wrapped in a `Result`.
* Useful for applying multiple arguments to a function in a safe way.
* @param resultFn - The `Result` containing the function.
* @param resultVal - The `Result` containing the value.
* @returns A new `Result` with the result of applying the function to the value.
* @public
*/
ap: (e, r) => s.isSuccess(e) && s.isSuccess(r) ? s.success(e.value(r.value)) : s.isFailure(e) ? s.failure(e.error) : s.failure(r.error),
/**
* Maps two `Result` values using a function.
* @param r1 - The first `Result`.
* @param r2 - The second `Result`.
* @param f - The function to apply to both values.
* @returns A new `Result` with the result of applying the function to both values.
* @public
*/
map2: (e, r, u) => s.isSuccess(e) && s.isSuccess(r) ? s.success(u(e.value, r.value)) : s.isFailure(e) ? s.failure(e.error) : s.failure(r.error),
/**
* Maps three `Result` values using a function.
* @param r1 - The first `Result`.
* @param r2 - The second `Result`.
* @param r3 - The third `Result`.
* @param f - The function to apply to all three values.
* @returns A new `Result` with the result of applying the function to all three values.
* @public
*/
map3: (e, r, u, t) => s.isSuccess(e) && s.isSuccess(r) && s.isSuccess(u) ? s.success(t(e.value, r.value, u.value)) : s.isFailure(e) ? s.failure(e.error) : s.isFailure(r) ? s.failure(r.error) : s.failure(u.error),
/**
* Converts a Promise to a Result.
* @param p - The Promise to convert.
* @returns A Promise that resolves to a Result.
* @public
*/
ofPromise: async (e) => {
try {
const r = await e;
return s.success(r);
} catch (r) {
return s.failure(r instanceof Error ? r : new Error(String(r)));
}
},
/**
* Swaps the success and failure values of a Result.
* A success becomes a failure with the value as the error,
* and a failure becomes a success with the error as the value.
* @param r - The Result to swap.
* @returns A new Result with swapped success and failure.
* @public
*/
swap: (e) => s.isSuccess(e) ? s.failure(e.value) : s.success(e.error),
/**
* Converts a nullable value to a Result.
* @param value - The nullable value.
* @param error - The error to use if the value is null or undefined.
* @returns A Result containing the value if not null/undefined, otherwise a failure.
* @public
*/
fromNullable: (e, r) => e == null ? s.failure(r) : s.success(e),
/**
* Converts a nullable value to a Result using a lazy error function.
* @param value - The nullable value.
* @param errorFn - The function to call to get the error if the value is null or undefined.
* @returns A Result containing the value if not null/undefined, otherwise a failure.
* @public
*/
fromNullableLazy: (e, r) => e == null ? s.failure(r()) : s.success(e),
/**
* Wraps a function that may throw into a function that returns a Result.
* @param f - The function that may throw.
* @returns A function that returns a Result instead of throwing.
* @public
*/
tryCatch: (e) => {
try {
return s.success(e());
} catch (r) {
return s.failure(r instanceof Error ? r : new Error(String(r)));
}
}
};
export {
s as R,
i as V
};