doddle
Version:
Tiny yet feature-packed (async) iteration toolkit.
202 lines (192 loc) • 8.48 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.reduceOnEmptyError = exports.invalidRecursionError = exports.gotAsyncIteratorInSyncContext = exports.checkASeqInputValue = exports.checkSeqInputValue = exports.forOperator = exports.getSubject = exports.DoddleError = void 0;
exports.loadCheckers = loadCheckers;
exports.chk = chk;
const utils_js_1 = require("../utils.js");
class DoddleError extends Error {
constructor(message) {
super((!Array.isArray(message) ? [message] : message).flat(5).join(" "));
}
}
exports.DoddleError = DoddleError;
/*
Sample error messages:
> Doddle: Argument 'predicate' of oprator 'Seq.filter' must be a function,
> but got 'true'.
> Doddle: Optional argument 'projection' of operator 'Seq.window' must be a function,
> but got 1.
> Doddle: Function argument 'predicate' to operator 'Seq.filter' must return
> true or false, but got 1.
> Doddle: {conversion|operator} 'X' must be called with {A-B|A} arguments, but got Z.
> Doddle: Input of conversion 'aseq' must be an async iterable, iterable, or a function,
> but got "hello world"
> Doddle: Each element of array argument 'others' to operator 'Seq.zip' must be an
> (async) iterable or function, but got "hello world" at index 1.
> OVERALL
> ${subject} must ${verb} ${expectation}, but got ${value} ${suffix}
subject.must
# Subject
- ${descriptor} '${name}' ${context}
# Descriptors
- Argument
- Function argument
# context
- of operator '${operator}'
-
*/
const getButGot = (value) => {
return ["but got", (0, utils_js_1.getValueDesc)(value)];
};
const getSubject = (thing, context, verb) => {
return [thing, "of", context, "must", verb];
};
exports.getSubject = getSubject;
const expectation = (expectation, check) => {
return (start) => (x) => {
if (!check(x)) {
const msg = [start, expectation, getButGot(x)];
throw new DoddleError(msg);
}
return x;
};
};
const expectInt = expectation(`a integer`, utils_js_1.isInt);
const expectString = expectation(`a string`, x => typeof x === "string");
const expectIntOrInfinity = expectation(`an integer or Infinity`, x => (0, utils_js_1.isInt)(x) || x === Infinity);
const expectPosInt = expectation(`a positive integer`, utils_js_1.isPosInt);
const expectBool = expectation(`true or false`, utils_js_1.isBool);
const expectError = expectation(`an error`, x => x instanceof Error);
const expectFunc = expectation(`a function`, utils_js_1.isFunction);
const expectPair = expectation(`an array of length 2`, utils_js_1.isPair);
const expectStage = expectation("'before', 'after', 'both', or undefined", stage => utils_js_1.orderedStages.indexOf(stage) > -1);
const anOrStructure = (a, b) => ["an", a, "or", b];
const expectSyncInputValue = expectation(anOrStructure(["iterable", "iterator", "doddle"], "a function"), x => (0, utils_js_1.isIterable)(x) || (0, utils_js_1.isFunction)(x) || (0, utils_js_1.isDoddle)(x) || (0, utils_js_1.isNextable)(x) || (0, utils_js_1.isArrayLike)(x));
const expectAsyncInputValue = expectation(anOrStructure(["(async) iterable", "iterator", "doddle"].join(", "), "a function"), x => (0, utils_js_1.isIterable)(x) ||
(0, utils_js_1.isAsyncIterable)(x) ||
(0, utils_js_1.isFunction)(x) ||
(0, utils_js_1.isDoddle)(x) ||
(0, utils_js_1.isNextable)(x) ||
(0, utils_js_1.isReadableStream)(x) ||
(0, utils_js_1.isArrayLike)(x));
const iterableOrIterator = anOrStructure("iterable", "iterator");
const expectSyncIterableOrIterator = expectation(anOrStructure("iterable", "iterator"), x => (0, utils_js_1.isIterable)(x) || (0, utils_js_1.isNextable)(x) || (0, utils_js_1.isDoddle)(x) || (0, utils_js_1.isArrayLike)(x));
const expectAsyncIterableOrIterator = expectation(anOrStructure(["(async)", "iterable"], "iterator"), x => (0, utils_js_1.isIterable)(x) ||
(0, utils_js_1.isAsyncIterable)(x) ||
(0, utils_js_1.isNextable)(x) ||
(0, utils_js_1.isDoddle)(x) ||
(0, utils_js_1.isReadableStream)(x) ||
(0, utils_js_1.isArrayLike)(x));
const checkFunctionReturn = (thing, context, expectReturn, allowAsync) => {
return (f) => {
expectFunc((0, exports.getSubject)(thing, context, "be"))(f);
return (...args) => {
const result = f(...args);
const resultChecker = expectReturn((0, exports.getSubject)(["function", thing], context, "return"));
if ((0, utils_js_1.isThenable)(result) && allowAsync) {
return result.then(x => {
if ((0, utils_js_1.isDoddle)(x)) {
return x.map(resultChecker);
}
return resultChecker(x);
});
}
if ((0, utils_js_1.isDoddle)(result)) {
return result.map(resultChecker);
}
return resultChecker(result);
};
};
};
const forOperator = (operator) => {
const context = ["operator", `'${operator}'`];
function getArgThing(name) {
return ["argument", `'${name}'`];
}
const allowAsync = ["a", "A"].includes(operator[0]);
function getArgSubject(name) {
return (0, exports.getSubject)(getArgThing(name), context, "be");
}
function checkValue(name, exp) {
return [name, exp(getArgSubject(name))];
}
function checkFuncReturn(name, exp) {
return [name, checkFunctionReturn(getArgThing(name), context, exp, allowAsync)];
}
const simpleEntries = [
checkValue("size", expectPosInt),
checkValue("start", expectInt),
checkValue("end", expectIntOrInfinity),
checkValue("index", expectInt),
checkValue("count", expectIntOrInfinity),
checkValue("projection", expectFunc),
checkValue("action", expectFunc),
checkValue("handler", expectFunc),
checkValue("separator", expectString),
checkValue("descending", expectBool),
checkValue("reducer", expectFunc),
checkValue("stage", expectStage),
checkValue("skipCount", expectPosInt),
checkValue("keyProjection", expectFunc),
checkFuncReturn("kvpProjection", expectPair),
checkFuncReturn("predicate", expectBool),
checkFuncReturn("thrower", expectError),
checkValue("ms", expectInt)
];
return Object.fromEntries(simpleEntries);
};
exports.forOperator = forOperator;
const wInput = "input";
const wSeq = "'seq'";
const wAseq = "'aseq'";
const checkSeqInputValue = (input) => {
const context = ["conversion", wSeq];
expectSyncInputValue((0, exports.getSubject)(wInput, context, "be"))(input);
if ((0, utils_js_1.isFunction)(input)) {
return checkFunctionReturn(wInput, context, expectSyncIterableOrIterator, false)(input);
}
return input;
};
exports.checkSeqInputValue = checkSeqInputValue;
const checkASeqInputValue = (input) => {
const context = ["conversion", wAseq];
expectAsyncInputValue((0, exports.getSubject)(wInput, context, "be"))(input);
if ((0, utils_js_1.isFunction)(input)) {
return checkFunctionReturn(wInput, context, expectAsyncIterableOrIterator, true)(input);
}
return input;
};
exports.checkASeqInputValue = checkASeqInputValue;
const gotAsyncIteratorInSyncContext = () => {
throw new DoddleError([
(0, exports.getSubject)(wInput, [wAseq], "be"),
iterableOrIterator,
["but got", "an async", "iterator"]
]);
};
exports.gotAsyncIteratorInSyncContext = gotAsyncIteratorInSyncContext;
const __checkers = "__checkers";
const LOADED = Symbol(__checkers);
function loadCheckers(target) {
if (LOADED in target) {
return target;
}
for (const key of Object.getOwnPropertyNames(target).filter(x => !x.startsWith("_"))) {
const v = target[key];
if ((0, utils_js_1.isFunction)(v) && !v[__checkers]) {
Object.defineProperty(v, __checkers, {
value: (0, exports.forOperator)(`${(0, utils_js_1.getClassName)(target)}.${key}`)
});
}
}
return Object.defineProperty(target, LOADED, {});
}
function chk(input) {
return input[__checkers];
}
const invalidRecursionError = (parentType) => {
return `Child iterable called its own ${parentType} during iteration, which is illegal.`;
};
exports.invalidRecursionError = invalidRecursionError;
exports.reduceOnEmptyError = "Cannot reduce empty sequence with no initial value";
//# sourceMappingURL=error.js.map