@quenk/preconditions
Version:
Make data satisfy constraints before using.
236 lines • 8.73 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.tee = exports.cast = exports.type = exports.typeOf = exports.log = exports.caseOf = exports.match = exports.enum = exports.isin = exports.exists = exports.anyOf = exports.every = exports.and = exports.or = exports.reject = exports.discard = exports.identity = exports.default = exports.defaultValue = exports.optional = exports.notNull = exports.neq = exports.eq = exports.whenFalse = exports.whenTrue = exports.when = exports.const = exports.constant = void 0;
/**
* The precondition library provides an API for validating
* whether some value meets a pre-condition before it is used
* in a program.
*
* Users of this library are expected to design their own preconditions,
* however some primitivies are provided to make things easier.
*/
const either_1 = require("@quenk/noni/lib/data/either");
const record_1 = require("@quenk/noni/lib/data/record");
const type_1 = require("@quenk/noni/lib/data/type");
const failure_1 = require("./result/failure");
const result_1 = require("./result");
const array_1 = require("./array");
const number_1 = require("./number");
const string_1 = require("./string");
/**
* constant forces the value to be the supplied value.
*/
const constant = (b) => (_) => (0, result_1.succeed)(b);
exports.constant = constant;
exports.const = exports.constant;
/**
* when conditionally applies one of two preconditions depending
* on the outcome of a test function.
*/
const when = (test, applied, otherwise) => (value) => test(value) === true ? applied(value) : otherwise(value);
exports.when = when;
/**
* whenTrue conditionally applies "applied" or "otherwise" depending
* on whether "condition" is true or not.
*/
const whenTrue = (condition, applied, otherwise) => (value) => condition === true ? applied(value) : otherwise(value);
exports.whenTrue = whenTrue;
/**
* whenFalse (opposite of whenTrue).
*/
const whenFalse = (condition, applied, otherwise) => (value) => condition === false ? applied(value) : otherwise(value);
exports.whenFalse = whenFalse;
/**
* eq tests if the value is equal (strictly) to the target.
*/
const eq = (target) => (value) => target === value
? (0, result_1.succeed)(target)
: (0, result_1.fail)('eq', value, { target });
exports.eq = eq;
/**
* neq tests if the value is not equal (strictly) to the target.
*/
const neq = (target) => (value) => target === value
? (0, result_1.fail)('neq', value, { target })
: (0, result_1.succeed)(target);
exports.neq = neq;
/**
* notNull will fail if the value is null or undefined.
*/
const notNull = (value) => value == null || (typeof value === 'string' && value === '')
? (0, result_1.fail)('notNull', value)
: (0, result_1.succeed)(value);
exports.notNull = notNull;
/**
* optional applies the precondition given only if the value is not null
* or undefined.
*/
const optional = (p) => (value) => value == null || (typeof value === 'string' && value === '')
? (0, result_1.succeed)(value)
: p(value);
exports.optional = optional;
/**
* defaultValue assigns a default value if the value to the precondition is
* nullable.
*/
const defaultValue = (fallback) => (value) => (0, result_1.succeed)(value == null ? fallback : value);
exports.defaultValue = defaultValue;
exports.default = exports.defaultValue;
/**
* identity always succeeds with the value it is applied to.
*/
const identity = (value) => (0, result_1.succeed)(value);
exports.identity = identity;
/**
* discard throws away a value by assigning it ot undefined.
*/
const discard = (_) => (0, result_1.succeed)(undefined);
exports.discard = discard;
/**
* reject always fails with reason no matter the value supplied.
*/
const reject = (reason) => (value) => (0, result_1.fail)(reason, value);
exports.reject = reject;
/**
* or performs the equivalent of a logical 'or' between two preconditions.
*/
const or = (left, right) => (value) => left(value).orElse(f => right(value).lmap(f2 => new failure_1.DualFailure(value, f, f2)));
exports.or = or;
/**
* and performs the equivalent of a logical 'and' between two preconditions.
*/
const and = (l, r) => (value) => {
let result = l(value);
if (result instanceof either_1.Left) {
return (0, either_1.left)(result.takeLeft());
}
else {
let result2 = r(result.takeRight());
if (result2 instanceof either_1.Left)
return (0, either_1.left)(failure_1.ModifiedFailure.create(value, result2.takeLeft()));
return (0, either_1.right)(result2.takeRight());
}
};
exports.and = and;
/**
* every takes a set of preconditions and attempts to apply each
* one after the other to the input.
*/
const every = (p, ...list) => (value) => {
let r = p(value);
if (r instanceof either_1.Left)
return r;
let r2 = list.reduce((p, c) => p.chain(c), (0, either_1.right)(r.takeRight()));
if (r2 instanceof either_1.Left)
return (0, either_1.left)(failure_1.ModifiedFailure.create(value, r2.takeLeft()));
return (0, either_1.right)(r2.takeRight());
};
exports.every = every;
/**
* anyOf applies all of the preconditions provided until one succeeds.
*/
const anyOf = (...list) => (value) => {
let result = (0, result_1.fail)('anyOf', value);
for (let i = 0; i < list.length; i++) {
result = list[i](value);
if (result.isRight())
break;
}
return result;
};
exports.anyOf = anyOf;
/**
* exists requires the value to be enumerated in the supplied list.
*/
const exists = (list) => (value) => list.indexOf(value) < 0
? (0, result_1.fail)('exists', value, { value, list })
: (0, result_1.succeed)(value);
exports.exists = exists;
/**
* isin requires the value passed to be a member of a provided list.
*/
const isin = (list) => (value) => list.indexOf(value) > -1
? (0, result_1.succeed)(value)
: (0, result_1.fail)('isin', value);
exports.isin = isin;
exports.enum = exports.isin;
/**
* match preforms a type/structure matching on the input
* value in order to decide which precondition to apply.
*
* Preconditions must be wrapped in a 'caseOf' precondition.
*/
const match = (p, ...list) => (value) => list.reduce((e, f) => e instanceof either_1.Right
? e
: e.orElse(r => (r.message === 'caseOf' ? f(value) : e)), p(value));
exports.match = match;
/**
* caseOf allows for the selective application of a precondition
* based on the type or structure of the value.
*
* Pattern matching works as follows:
* string -> Matches on the value of the string.
* number -> Matches on the value of the number.
* boolean -> Matches on the value of the boolean.
* object -> Each key of the object is matched on the value, all must match.
* function -> Treated as a constructor and results in an instanceof check.
* For String,Number and Boolean, this uses the typeof check.
*/
const caseOf = (t, p) => (value) => (0, type_1.test)(value, t) ? p(value) : (0, result_1.fail)('caseOf', value, { type: t });
exports.caseOf = caseOf;
/**
* log the value to the console.
*/
const log = (value) => (console.log(value), (0, result_1.succeed)(value));
exports.log = log;
function typeOf(type) {
return (value) => {
let result;
if (type === 'array')
result = Array.isArray(value);
else if (type === 'object')
result = (0, type_1.isObject)(value);
else
result = typeof value === type;
return result
? (0, result_1.succeed)(value)
: (0, result_1.fail)(type, value, { type });
};
}
exports.typeOf = typeOf;
exports.type = typeOf;
function cast(type) {
switch (type) {
case 'object':
return value => (0, result_1.succeed)(((0, record_1.isRecord)(value) ? value : {}));
case 'array':
return array_1.toArray;
case 'string':
return string_1.toString;
case 'number':
return number_1.toNumber;
case 'boolean':
return (value) => (0, result_1.succeed)(Boolean(value));
}
}
exports.cast = cast;
/**
* tee applies each of the provided preconditions to a single value.
*
* This results in a list of values each corresponding to a value in the array
* upon success. This precondition fails on the first failed precondition
* encountered.
*/
const tee = (precs) => (value) => {
let results = [];
for (let i = 0; i < precs.length; i++) {
let prec = precs[i];
let result = prec(value);
if (result.isLeft())
return result;
results.push(result.takeRight());
}
return (0, result_1.succeed)(results);
};
exports.tee = tee;
//# sourceMappingURL=index.js.map