UNPKG

doddle

Version:

Tiny yet feature-packed (async) iteration toolkit.

202 lines (192 loc) 8.48 kB
"use strict"; 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