@rimbu/deep
Advanced tools
Comparing version 0.12.1 to 0.13.0
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.match = void 0; | ||
var tslib_1 = require("tslib"); | ||
var base_1 = require("@rimbu/base"); | ||
@@ -11,3 +12,3 @@ /** | ||
* @param matcher - a matcher object or a function taking the matcher API and returning a match object | ||
* @param errorCollector - (optional) a string array that can be passed to collect reasons why the match failed | ||
* @param failureLog - (optional) a string array that can be passed to collect reasons why the match failed | ||
* @example | ||
@@ -25,5 +26,4 @@ * ```ts | ||
*/ | ||
function match(source, matcher, errorCollector) { | ||
if (errorCollector === void 0) { errorCollector = undefined; } | ||
return matchEntry(source, source, source, matcher, errorCollector); | ||
function match(source, matcher, failureLog) { | ||
return matchEntry(source, source, source, matcher, failureLog); | ||
} | ||
@@ -34,3 +34,3 @@ exports.match = match; | ||
*/ | ||
function matchEntry(source, parent, root, matcher, errorCollector) { | ||
function matchEntry(source, parent, root, matcher, failureLog) { | ||
if (Object.is(source, matcher)) { | ||
@@ -43,3 +43,3 @@ // value and target are exactly the same, always will be true | ||
// they are not equal | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push("value ".concat(JSON.stringify(source), " did not match matcher ").concat(matcher)); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push("value ".concat(JSON.stringify(source), " did not match matcher ").concat(matcher)); | ||
return false; | ||
@@ -51,3 +51,3 @@ } | ||
if (!result) { | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push("both value and matcher are functions, but they do not have the same reference"); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push("both value and matcher are functions, but they do not have the same reference"); | ||
} | ||
@@ -62,3 +62,3 @@ return result; | ||
if (!matcherResult) { | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push("function matcher returned false for value ".concat(JSON.stringify(source))); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push("function matcher returned false for value ".concat(JSON.stringify(source))); | ||
} | ||
@@ -68,14 +68,14 @@ return matcherResult; | ||
// function resulted in a value that needs to be further matched | ||
return matchEntry(source, parent, root, matcherResult, errorCollector); | ||
return matchEntry(source, parent, root, matcherResult, failureLog); | ||
} | ||
if ((0, base_1.isPlainObj)(source)) { | ||
// source ia a plain object, can be partially matched | ||
return matchPlainObj(source, parent, root, matcher, errorCollector); | ||
return matchPlainObj(source, parent, root, matcher, failureLog); | ||
} | ||
if (Array.isArray(source)) { | ||
// source is an array | ||
return matchArr(source, root, matcher, errorCollector); | ||
return matchArr(source, parent, root, matcher, failureLog); | ||
} | ||
// already determined above that the source and matcher are not equal | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push("value ".concat(JSON.stringify(source), " does not match given matcher ").concat(JSON.stringify(matcher))); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push("value ".concat(JSON.stringify(source), " does not match given matcher ").concat(JSON.stringify(matcher))); | ||
return false; | ||
@@ -86,3 +86,3 @@ } | ||
*/ | ||
function matchArr(source, root, matcher, errorCollector) { | ||
function matchArr(source, parent, root, matcher, failureLog) { | ||
if (Array.isArray(matcher)) { | ||
@@ -93,3 +93,3 @@ // directly compare array contents | ||
// if lengths not equal, arrays are not equal | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push("array lengths are not equal: value length ".concat(source.length, " !== matcher length ").concat(matcher.length)); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push("array lengths are not equal: value length ".concat(source.length, " !== matcher length ").concat(matcher.length)); | ||
return false; | ||
@@ -100,5 +100,5 @@ } | ||
while (++index < length_1) { | ||
if (!Object.is(source[index], matcher[index])) { | ||
if (!matchEntry(source[index], source, root, matcher[index], failureLog)) { | ||
// item did not match, return false | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push("index ".concat(index, " does not match with value ").concat(JSON.stringify(source[index]), " and matcher ").concat(matcher[index])); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push("index ".concat(index, " does not match with value ").concat(JSON.stringify(source[index]), " and matcher ").concat(matcher[index])); | ||
return false; | ||
@@ -110,2 +110,27 @@ } | ||
} | ||
// matcher is plain object | ||
if ("every" in matcher) { | ||
return matchCompound(source, parent, root, tslib_1.__spreadArray(['every'], tslib_1.__read(matcher.every), false), failureLog); | ||
} | ||
if ("some" in matcher) { | ||
return matchCompound(source, parent, root, tslib_1.__spreadArray(['some'], tslib_1.__read(matcher.some), false), failureLog); | ||
} | ||
if ("none" in matcher) { | ||
return matchCompound(source, parent, root, tslib_1.__spreadArray(['none'], tslib_1.__read(matcher.none), false), failureLog); | ||
} | ||
if ("single" in matcher) { | ||
return matchCompound(source, parent, root, tslib_1.__spreadArray(['single'], tslib_1.__read(matcher.single), false), failureLog); | ||
} | ||
if ("someItem" in matcher) { | ||
return matchTraversal(source, root, 'someItem', matcher.someItem, failureLog); | ||
} | ||
if ("everyItem" in matcher) { | ||
return matchTraversal(source, root, 'everyItem', matcher.everyItem, failureLog); | ||
} | ||
if ("noneItem" in matcher) { | ||
return matchTraversal(source, root, 'noneItem', matcher.noneItem, failureLog); | ||
} | ||
if ("singleItem" in matcher) { | ||
return matchTraversal(source, root, 'singleItem', matcher.singleItem, failureLog); | ||
} | ||
// matcher is plain object with index keys | ||
@@ -116,10 +141,10 @@ for (var index in matcher) { | ||
// source does not have item at given index | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push("index ".concat(index, " does not exist in source ").concat(JSON.stringify(source), " but should match matcher ").concat(JSON.stringify(matcherAtIndex))); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push("index ".concat(index, " does not exist in source ").concat(JSON.stringify(source), " but should match matcher ").concat(JSON.stringify(matcherAtIndex))); | ||
return false; | ||
} | ||
// match the source item at the given index | ||
var result = matchEntry(source[index], source, root, matcherAtIndex, errorCollector); | ||
var result = matchEntry(source[index], source, root, matcherAtIndex, failureLog); | ||
if (!result) { | ||
// item did not match | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push("index ".concat(index, " does not match with value ").concat(JSON.stringify(source[index]), " and matcher ").concat(JSON.stringify(matcherAtIndex))); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push("index ".concat(index, " does not match with value ").concat(JSON.stringify(source[index]), " and matcher ").concat(JSON.stringify(matcherAtIndex))); | ||
return false; | ||
@@ -134,6 +159,6 @@ } | ||
*/ | ||
function matchPlainObj(source, parent, root, matcher, errorCollector) { | ||
function matchPlainObj(source, parent, root, matcher, failureLog) { | ||
if (Array.isArray(matcher)) { | ||
// the matcher is of compound type | ||
return matchCompound(source, parent, root, matcher, errorCollector); | ||
return matchCompound(source, parent, root, matcher, failureLog); | ||
} | ||
@@ -144,9 +169,9 @@ // partial object props matcher | ||
// the source does not have the given key | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push("key ".concat(key, " is specified in matcher but not present in value ").concat(JSON.stringify(source))); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push("key ".concat(key, " is specified in matcher but not present in value ").concat(JSON.stringify(source))); | ||
return false; | ||
} | ||
// match the source value at the given key with the matcher at given key | ||
var result = matchEntry(source[key], source, root, matcher[key], errorCollector); | ||
var result = matchEntry(source[key], source, root, matcher[key], failureLog); | ||
if (!result) { | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push("key ".concat(key, " does not match in value ").concat(JSON.stringify(source[key]), " with matcher ").concat(JSON.stringify(matcher[key]))); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push("key ".concat(key, " does not match in value ").concat(JSON.stringify(source[key]), " with matcher ").concat(JSON.stringify(matcher[key]))); | ||
return false; | ||
@@ -161,3 +186,3 @@ } | ||
*/ | ||
function matchCompound(source, parent, root, compound, errorCollector) { | ||
function matchCompound(source, parent, root, compound, failureLog) { | ||
// first item indicates compound match type | ||
@@ -172,5 +197,5 @@ var matchType = compound[0]; | ||
// if any item does not match, return false | ||
var result = matchEntry(source, parent, root, compound[index], errorCollector); | ||
var result = matchEntry(source, parent, root, compound[index], failureLog); | ||
if (!result) { | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push("in compound \"every\": match at index ".concat(index, " failed")); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push("in compound \"every\": match at index ".concat(index, " failed")); | ||
return false; | ||
@@ -184,5 +209,5 @@ } | ||
while (++index < length) { | ||
var result = matchEntry(source, parent, root, compound[index], errorCollector); | ||
var result = matchEntry(source, parent, root, compound[index], failureLog); | ||
if (result) { | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push("in compound \"none\": match at index ".concat(index, " succeeded")); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push("in compound \"none\": match at index ".concat(index, " succeeded")); | ||
return false; | ||
@@ -197,6 +222,6 @@ } | ||
while (++index < length) { | ||
var result = matchEntry(source, parent, root, compound[index], errorCollector); | ||
var result = matchEntry(source, parent, root, compound[index], failureLog); | ||
if (result) { | ||
if (onePassed) { | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push("in compound \"single\": multiple matches succeeded"); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push("in compound \"single\": multiple matches succeeded"); | ||
return false; | ||
@@ -208,3 +233,3 @@ } | ||
if (!onePassed) { | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push("in compound \"single\": no matches succeeded"); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push("in compound \"single\": no matches succeeded"); | ||
} | ||
@@ -216,3 +241,3 @@ return onePassed; | ||
while (++index < length) { | ||
var result = matchEntry(source, parent, root, compound[index], errorCollector); | ||
var result = matchEntry(source, parent, root, compound[index], failureLog); | ||
if (result) { | ||
@@ -222,3 +247,3 @@ return true; | ||
} | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push("in compound \"some\": no matches succeeded"); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push("in compound \"some\": no matches succeeded"); | ||
return false; | ||
@@ -228,2 +253,52 @@ } | ||
} | ||
function matchTraversal(source, root, matchType, matcher, failureLog) { | ||
var index = -1; | ||
var length = source.length; | ||
switch (matchType) { | ||
case 'someItem': { | ||
while (++index < length) { | ||
if (matchEntry(source[index], source, root, matcher, failureLog)) { | ||
return true; | ||
} | ||
} | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push("in array traversal \"someItem\": no items matched given matcher"); | ||
return false; | ||
} | ||
case 'everyItem': { | ||
while (++index < length) { | ||
if (!matchEntry(source[index], source, root, matcher, failureLog)) { | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push("in array traversal \"everyItem\": at least one item did not match given matcher"); | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
case 'noneItem': { | ||
while (++index < length) { | ||
if (matchEntry(source[index], source, root, matcher, failureLog)) { | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push("in array traversal \"noneItem\": at least one item matched given matcher"); | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
case 'singleItem': { | ||
var singleMatched = false; | ||
while (++index < length) { | ||
if (matchEntry(source[index], source, root, matcher, failureLog)) { | ||
if (singleMatched) { | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push("in array traversal \"singleItem\": more than one item matched given matcher"); | ||
return false; | ||
} | ||
singleMatched = true; | ||
} | ||
} | ||
if (!singleMatched) { | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push("in array traversal \"singleItem\": no item matched given matcher"); | ||
return false; | ||
} | ||
return true; | ||
} | ||
} | ||
} | ||
//# sourceMappingURL=match.js.map |
@@ -8,3 +8,3 @@ import { isPlainObj, } from '@rimbu/base'; | ||
* @param matcher - a matcher object or a function taking the matcher API and returning a match object | ||
* @param errorCollector - (optional) a string array that can be passed to collect reasons why the match failed | ||
* @param failureLog - (optional) a string array that can be passed to collect reasons why the match failed | ||
* @example | ||
@@ -22,4 +22,4 @@ * ```ts | ||
*/ | ||
export function match(source, matcher, errorCollector = undefined) { | ||
return matchEntry(source, source, source, matcher, errorCollector); | ||
export function match(source, matcher, failureLog) { | ||
return matchEntry(source, source, source, matcher, failureLog); | ||
} | ||
@@ -29,3 +29,3 @@ /** | ||
*/ | ||
function matchEntry(source, parent, root, matcher, errorCollector) { | ||
function matchEntry(source, parent, root, matcher, failureLog) { | ||
if (Object.is(source, matcher)) { | ||
@@ -38,3 +38,3 @@ // value and target are exactly the same, always will be true | ||
// they are not equal | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`value ${JSON.stringify(source)} did not match matcher ${matcher}`); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push(`value ${JSON.stringify(source)} did not match matcher ${matcher}`); | ||
return false; | ||
@@ -46,3 +46,3 @@ } | ||
if (!result) { | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`both value and matcher are functions, but they do not have the same reference`); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push(`both value and matcher are functions, but they do not have the same reference`); | ||
} | ||
@@ -57,3 +57,3 @@ return result; | ||
if (!matcherResult) { | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`function matcher returned false for value ${JSON.stringify(source)}`); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push(`function matcher returned false for value ${JSON.stringify(source)}`); | ||
} | ||
@@ -63,14 +63,14 @@ return matcherResult; | ||
// function resulted in a value that needs to be further matched | ||
return matchEntry(source, parent, root, matcherResult, errorCollector); | ||
return matchEntry(source, parent, root, matcherResult, failureLog); | ||
} | ||
if (isPlainObj(source)) { | ||
// source ia a plain object, can be partially matched | ||
return matchPlainObj(source, parent, root, matcher, errorCollector); | ||
return matchPlainObj(source, parent, root, matcher, failureLog); | ||
} | ||
if (Array.isArray(source)) { | ||
// source is an array | ||
return matchArr(source, root, matcher, errorCollector); | ||
return matchArr(source, parent, root, matcher, failureLog); | ||
} | ||
// already determined above that the source and matcher are not equal | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`value ${JSON.stringify(source)} does not match given matcher ${JSON.stringify(matcher)}`); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push(`value ${JSON.stringify(source)} does not match given matcher ${JSON.stringify(matcher)}`); | ||
return false; | ||
@@ -81,3 +81,3 @@ } | ||
*/ | ||
function matchArr(source, root, matcher, errorCollector) { | ||
function matchArr(source, parent, root, matcher, failureLog) { | ||
if (Array.isArray(matcher)) { | ||
@@ -88,3 +88,3 @@ // directly compare array contents | ||
// if lengths not equal, arrays are not equal | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`array lengths are not equal: value length ${source.length} !== matcher length ${matcher.length}`); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push(`array lengths are not equal: value length ${source.length} !== matcher length ${matcher.length}`); | ||
return false; | ||
@@ -95,5 +95,5 @@ } | ||
while (++index < length) { | ||
if (!Object.is(source[index], matcher[index])) { | ||
if (!matchEntry(source[index], source, root, matcher[index], failureLog)) { | ||
// item did not match, return false | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`index ${index} does not match with value ${JSON.stringify(source[index])} and matcher ${matcher[index]}`); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push(`index ${index} does not match with value ${JSON.stringify(source[index])} and matcher ${matcher[index]}`); | ||
return false; | ||
@@ -105,2 +105,27 @@ } | ||
} | ||
// matcher is plain object | ||
if (`every` in matcher) { | ||
return matchCompound(source, parent, root, ['every', ...matcher.every], failureLog); | ||
} | ||
if (`some` in matcher) { | ||
return matchCompound(source, parent, root, ['some', ...matcher.some], failureLog); | ||
} | ||
if (`none` in matcher) { | ||
return matchCompound(source, parent, root, ['none', ...matcher.none], failureLog); | ||
} | ||
if (`single` in matcher) { | ||
return matchCompound(source, parent, root, ['single', ...matcher.single], failureLog); | ||
} | ||
if (`someItem` in matcher) { | ||
return matchTraversal(source, root, 'someItem', matcher.someItem, failureLog); | ||
} | ||
if (`everyItem` in matcher) { | ||
return matchTraversal(source, root, 'everyItem', matcher.everyItem, failureLog); | ||
} | ||
if (`noneItem` in matcher) { | ||
return matchTraversal(source, root, 'noneItem', matcher.noneItem, failureLog); | ||
} | ||
if (`singleItem` in matcher) { | ||
return matchTraversal(source, root, 'singleItem', matcher.singleItem, failureLog); | ||
} | ||
// matcher is plain object with index keys | ||
@@ -111,10 +136,10 @@ for (const index in matcher) { | ||
// source does not have item at given index | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`index ${index} does not exist in source ${JSON.stringify(source)} but should match matcher ${JSON.stringify(matcherAtIndex)}`); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push(`index ${index} does not exist in source ${JSON.stringify(source)} but should match matcher ${JSON.stringify(matcherAtIndex)}`); | ||
return false; | ||
} | ||
// match the source item at the given index | ||
const result = matchEntry(source[index], source, root, matcherAtIndex, errorCollector); | ||
const result = matchEntry(source[index], source, root, matcherAtIndex, failureLog); | ||
if (!result) { | ||
// item did not match | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`index ${index} does not match with value ${JSON.stringify(source[index])} and matcher ${JSON.stringify(matcherAtIndex)}`); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push(`index ${index} does not match with value ${JSON.stringify(source[index])} and matcher ${JSON.stringify(matcherAtIndex)}`); | ||
return false; | ||
@@ -129,6 +154,6 @@ } | ||
*/ | ||
function matchPlainObj(source, parent, root, matcher, errorCollector) { | ||
function matchPlainObj(source, parent, root, matcher, failureLog) { | ||
if (Array.isArray(matcher)) { | ||
// the matcher is of compound type | ||
return matchCompound(source, parent, root, matcher, errorCollector); | ||
return matchCompound(source, parent, root, matcher, failureLog); | ||
} | ||
@@ -139,9 +164,9 @@ // partial object props matcher | ||
// the source does not have the given key | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`key ${key} is specified in matcher but not present in value ${JSON.stringify(source)}`); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push(`key ${key} is specified in matcher but not present in value ${JSON.stringify(source)}`); | ||
return false; | ||
} | ||
// match the source value at the given key with the matcher at given key | ||
const result = matchEntry(source[key], source, root, matcher[key], errorCollector); | ||
const result = matchEntry(source[key], source, root, matcher[key], failureLog); | ||
if (!result) { | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`key ${key} does not match in value ${JSON.stringify(source[key])} with matcher ${JSON.stringify(matcher[key])}`); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push(`key ${key} does not match in value ${JSON.stringify(source[key])} with matcher ${JSON.stringify(matcher[key])}`); | ||
return false; | ||
@@ -156,3 +181,3 @@ } | ||
*/ | ||
function matchCompound(source, parent, root, compound, errorCollector) { | ||
function matchCompound(source, parent, root, compound, failureLog) { | ||
// first item indicates compound match type | ||
@@ -167,5 +192,5 @@ const matchType = compound[0]; | ||
// if any item does not match, return false | ||
const result = matchEntry(source, parent, root, compound[index], errorCollector); | ||
const result = matchEntry(source, parent, root, compound[index], failureLog); | ||
if (!result) { | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`in compound "every": match at index ${index} failed`); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push(`in compound "every": match at index ${index} failed`); | ||
return false; | ||
@@ -179,5 +204,5 @@ } | ||
while (++index < length) { | ||
const result = matchEntry(source, parent, root, compound[index], errorCollector); | ||
const result = matchEntry(source, parent, root, compound[index], failureLog); | ||
if (result) { | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`in compound "none": match at index ${index} succeeded`); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push(`in compound "none": match at index ${index} succeeded`); | ||
return false; | ||
@@ -192,6 +217,6 @@ } | ||
while (++index < length) { | ||
const result = matchEntry(source, parent, root, compound[index], errorCollector); | ||
const result = matchEntry(source, parent, root, compound[index], failureLog); | ||
if (result) { | ||
if (onePassed) { | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`in compound "single": multiple matches succeeded`); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push(`in compound "single": multiple matches succeeded`); | ||
return false; | ||
@@ -203,3 +228,3 @@ } | ||
if (!onePassed) { | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`in compound "single": no matches succeeded`); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push(`in compound "single": no matches succeeded`); | ||
} | ||
@@ -211,3 +236,3 @@ return onePassed; | ||
while (++index < length) { | ||
const result = matchEntry(source, parent, root, compound[index], errorCollector); | ||
const result = matchEntry(source, parent, root, compound[index], failureLog); | ||
if (result) { | ||
@@ -217,3 +242,3 @@ return true; | ||
} | ||
errorCollector === null || errorCollector === void 0 ? void 0 : errorCollector.push(`in compound "some": no matches succeeded`); | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push(`in compound "some": no matches succeeded`); | ||
return false; | ||
@@ -223,2 +248,52 @@ } | ||
} | ||
function matchTraversal(source, root, matchType, matcher, failureLog) { | ||
let index = -1; | ||
const length = source.length; | ||
switch (matchType) { | ||
case 'someItem': { | ||
while (++index < length) { | ||
if (matchEntry(source[index], source, root, matcher, failureLog)) { | ||
return true; | ||
} | ||
} | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push(`in array traversal "someItem": no items matched given matcher`); | ||
return false; | ||
} | ||
case 'everyItem': { | ||
while (++index < length) { | ||
if (!matchEntry(source[index], source, root, matcher, failureLog)) { | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push(`in array traversal "everyItem": at least one item did not match given matcher`); | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
case 'noneItem': { | ||
while (++index < length) { | ||
if (matchEntry(source[index], source, root, matcher, failureLog)) { | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push(`in array traversal "noneItem": at least one item matched given matcher`); | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
case 'singleItem': { | ||
let singleMatched = false; | ||
while (++index < length) { | ||
if (matchEntry(source[index], source, root, matcher, failureLog)) { | ||
if (singleMatched) { | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push(`in array traversal "singleItem": more than one item matched given matcher`); | ||
return false; | ||
} | ||
singleMatched = true; | ||
} | ||
} | ||
if (!singleMatched) { | ||
failureLog === null || failureLog === void 0 ? void 0 : failureLog.push(`in array traversal "singleItem": no item matched given matcher`); | ||
return false; | ||
} | ||
return true; | ||
} | ||
} | ||
} | ||
//# sourceMappingURL=match.js.map |
@@ -15,7 +15,6 @@ import { IsAnyFunc, IsArray, IsPlainObj, NotIterable } from '@rimbu/base'; | ||
* @typeparam C - utility type | ||
* @typeparam P - the parant type | ||
* @typeparam P - the parent type | ||
* @typeparam R - the root object type | ||
*/ | ||
type Entry<T, C, P, R> = IsAnyFunc<T> extends true ? T : IsPlainObj<T> extends true ? Match.WithResult<T, P, R, Match.Obj<T, C, P, R>> : IsArray<T> extends true ? // determine allowed match values for array or tuple | ||
Match.Arr<T, C, P, R> | Match.Func<T, P, R, Match.Arr<T, C, P, R>> : Match.WithResult<T, P, R, { | ||
type Entry<T, C, P, R> = IsAnyFunc<T> extends true ? T : IsPlainObj<T> extends true ? Match.WithResult<T, P, R, Match.Obj<T, C, P, R>> : IsArray<T> extends true ? Match.Arr<T, C, P, R> | Match.Entry<T[number & keyof T], C[number & keyof C], P, R>[] | Match.Func<T, P, R, Match.Arr<T, C, P, R> | Match.Entry<T[number & keyof T], C[number & keyof C], P, R>[]> : Match.WithResult<T, P, R, { | ||
[K in keyof C]: C[K & keyof T]; | ||
@@ -27,3 +26,3 @@ }>; | ||
* @typeparam C - utility type | ||
* @typeparam P - the parant type | ||
* @typeparam P - the parent type | ||
* @typeparam R - the root object type | ||
@@ -45,8 +44,16 @@ */ | ||
* @typeparam C - utility type | ||
* @typeparam P - the parant type | ||
* @typeparam P - the parent type | ||
* @typeparam R - the root object type | ||
*/ | ||
type Arr<T, C, P, R> = C | Match.CompoundForArr<T, C, P, R> | (Match.TupIndices<T, C, R> & { | ||
[K in Match.CompoundType]?: never; | ||
type Arr<T, C, P, R> = C | Match.CompoundForArr<T, C, P, R> | Match.TraversalForArr<T, C, R> | (Match.TupIndices<T, C, R> & { | ||
[K in Match.CompoundType | Match.ArrayTraversalType]?: never; | ||
}); | ||
/** | ||
* A type that either directly results in result type `S` or is a function taking the value, parent, and root values, and | ||
* returns a value of type `S`. | ||
* @typeparam T - the input value type | ||
* @typeparam P - the parent type | ||
* @typeparam R - the root object type | ||
* @typeparam S - the result type | ||
*/ | ||
type WithResult<T, P, R, S> = S | Match.Func<T, P, R, S>; | ||
@@ -56,3 +63,3 @@ /** | ||
* @typeparam T - the input value type | ||
* @typeparam P - the parant type | ||
* @typeparam P - the parent type | ||
* @typeparam R - the root object type | ||
@@ -76,2 +83,6 @@ * @typeparam S - the allowed return value type | ||
/** | ||
* Keys used to indicate an array match traversal. | ||
*/ | ||
type ArrayTraversalType = `${CompoundType}Item`; | ||
/** | ||
* Compount matcher for objects, can only be an array staring with a compound type keyword. | ||
@@ -95,10 +106,21 @@ * @typeparam T - the input value type | ||
type CompoundForArr<T, C, P, R> = { | ||
[K in CompoundType]: { | ||
[K2 in CompoundType]?: K2 extends K ? Match.Entry<T, C, P, R>[] : never; | ||
[K in Match.CompoundType]: { | ||
[K2 in Match.CompoundType]?: K2 extends K ? Match.Entry<T, C, P, R>[] : never; | ||
}; | ||
}[CompoundType]; | ||
}[Match.CompoundType]; | ||
/** | ||
* Utility type for collecting errors | ||
* Defines an object containing exactly one `TraversalType` key, having a matcher for the array element type. | ||
* @typeparam T - the input value type | ||
* @typeparam C - utility type | ||
* @typeparam R - the root object type | ||
*/ | ||
type ErrorCollector = string[] | undefined; | ||
type TraversalForArr<T, C, R> = { | ||
[K in Match.ArrayTraversalType]: { | ||
[K2 in Match.ArrayTraversalType]?: K2 extends K ? Match.Entry<T[number & keyof T], C[number & keyof C], T, R> : never; | ||
}; | ||
}[Match.ArrayTraversalType]; | ||
/** | ||
* Utility type for collecting match failure reasons | ||
*/ | ||
type FailureLog = string[]; | ||
} | ||
@@ -111,3 +133,3 @@ /** | ||
* @param matcher - a matcher object or a function taking the matcher API and returning a match object | ||
* @param errorCollector - (optional) a string array that can be passed to collect reasons why the match failed | ||
* @param failureLog - (optional) a string array that can be passed to collect reasons why the match failed | ||
* @example | ||
@@ -125,2 +147,2 @@ * ```ts | ||
*/ | ||
export declare function match<T, C extends Partial<T> = Partial<T>>(source: T, matcher: Match<T, C>, errorCollector?: Match.ErrorCollector): boolean; | ||
export declare function match<T, C extends Partial<T> = Partial<T>>(source: T, matcher: Match<T, C>, failureLog?: Match.FailureLog): boolean; |
{ | ||
"name": "@rimbu/deep", | ||
"version": "0.12.1", | ||
"version": "0.13.0", | ||
"description": "Tools to use handle plain JS objects as immutable objects", | ||
@@ -71,3 +71,3 @@ "keywords": [ | ||
}, | ||
"gitHead": "ef217d1ea7a8287bec3b4755fdb9d717318e1941" | ||
"gitHead": "92420ee29abdd7241014c9a5a10210c127f314d0" | ||
} |
284
src/match.ts
@@ -28,3 +28,3 @@ import { | ||
* @typeparam C - utility type | ||
* @typeparam P - the parant type | ||
* @typeparam P - the parent type | ||
* @typeparam R - the root object type | ||
@@ -40,3 +40,11 @@ */ | ||
? // determine allowed match values for array or tuple | ||
Match.Arr<T, C, P, R> | Match.Func<T, P, R, Match.Arr<T, C, P, R>> | ||
| Match.Arr<T, C, P, R> | ||
| Match.Entry<T[number & keyof T], C[number & keyof C], P, R>[] | ||
| Match.Func< | ||
T, | ||
P, | ||
R, | ||
| Match.Arr<T, C, P, R> | ||
| Match.Entry<T[number & keyof T], C[number & keyof C], P, R>[] | ||
> | ||
: // only accept values with same interface | ||
@@ -49,3 +57,3 @@ Match.WithResult<T, P, R, { [K in keyof C]: C[K & keyof T] }>; | ||
* @typeparam C - utility type | ||
* @typeparam P - the parant type | ||
* @typeparam P - the parent type | ||
* @typeparam R - the root object type | ||
@@ -71,3 +79,3 @@ */ | ||
* @typeparam C - utility type | ||
* @typeparam P - the parant type | ||
* @typeparam P - the parent type | ||
* @typeparam R - the root object type | ||
@@ -78,4 +86,15 @@ */ | ||
| Match.CompoundForArr<T, C, P, R> | ||
| (Match.TupIndices<T, C, R> & { [K in Match.CompoundType]?: never }); | ||
| Match.TraversalForArr<T, C, R> | ||
| (Match.TupIndices<T, C, R> & { | ||
[K in Match.CompoundType | Match.ArrayTraversalType]?: never; | ||
}); | ||
/** | ||
* A type that either directly results in result type `S` or is a function taking the value, parent, and root values, and | ||
* returns a value of type `S`. | ||
* @typeparam T - the input value type | ||
* @typeparam P - the parent type | ||
* @typeparam R - the root object type | ||
* @typeparam S - the result type | ||
*/ | ||
export type WithResult<T, P, R, S> = S | Match.Func<T, P, R, S>; | ||
@@ -86,3 +105,3 @@ | ||
* @typeparam T - the input value type | ||
* @typeparam P - the parant type | ||
* @typeparam P - the parent type | ||
* @typeparam R - the root object type | ||
@@ -113,2 +132,7 @@ * @typeparam S - the allowed return value type | ||
/** | ||
* Keys used to indicate an array match traversal. | ||
*/ | ||
export type ArrayTraversalType = `${CompoundType}Item`; | ||
/** | ||
* Compount matcher for objects, can only be an array staring with a compound type keyword. | ||
@@ -133,11 +157,27 @@ * @typeparam T - the input value type | ||
export type CompoundForArr<T, C, P, R> = { | ||
[K in CompoundType]: { | ||
[K2 in CompoundType]?: K2 extends K ? Match.Entry<T, C, P, R>[] : never; | ||
[K in Match.CompoundType]: { | ||
[K2 in Match.CompoundType]?: K2 extends K | ||
? Match.Entry<T, C, P, R>[] | ||
: never; | ||
}; | ||
}[CompoundType]; | ||
}[Match.CompoundType]; | ||
/** | ||
* Utility type for collecting errors | ||
* Defines an object containing exactly one `TraversalType` key, having a matcher for the array element type. | ||
* @typeparam T - the input value type | ||
* @typeparam C - utility type | ||
* @typeparam R - the root object type | ||
*/ | ||
export type ErrorCollector = string[] | undefined; | ||
export type TraversalForArr<T, C, R> = { | ||
[K in Match.ArrayTraversalType]: { | ||
[K2 in Match.ArrayTraversalType]?: K2 extends K | ||
? Match.Entry<T[number & keyof T], C[number & keyof C], T, R> | ||
: never; | ||
}; | ||
}[Match.ArrayTraversalType]; | ||
/** | ||
* Utility type for collecting match failure reasons | ||
*/ | ||
export type FailureLog = string[]; | ||
} | ||
@@ -151,3 +191,3 @@ | ||
* @param matcher - a matcher object or a function taking the matcher API and returning a match object | ||
* @param errorCollector - (optional) a string array that can be passed to collect reasons why the match failed | ||
* @param failureLog - (optional) a string array that can be passed to collect reasons why the match failed | ||
* @example | ||
@@ -168,5 +208,5 @@ * ```ts | ||
matcher: Match<T, C>, | ||
errorCollector: Match.ErrorCollector = undefined | ||
failureLog?: Match.FailureLog | ||
): boolean { | ||
return matchEntry(source, source, source, matcher as any, errorCollector); | ||
return matchEntry(source, source, source, matcher as any, failureLog); | ||
} | ||
@@ -182,3 +222,3 @@ | ||
matcher: Match.Entry<T, C, P, R>, | ||
errorCollector: Match.ErrorCollector | ||
failureLog?: Match.FailureLog | ||
): boolean { | ||
@@ -193,3 +233,3 @@ if (Object.is(source, matcher)) { | ||
// they are not equal | ||
errorCollector?.push( | ||
failureLog?.push( | ||
`value ${JSON.stringify(source)} did not match matcher ${matcher}` | ||
@@ -206,3 +246,3 @@ ); | ||
if (!result) { | ||
errorCollector?.push( | ||
failureLog?.push( | ||
`both value and matcher are functions, but they do not have the same reference` | ||
@@ -223,3 +263,3 @@ ); | ||
if (!matcherResult) { | ||
errorCollector?.push( | ||
failureLog?.push( | ||
`function matcher returned false for value ${JSON.stringify(source)}` | ||
@@ -233,3 +273,3 @@ ); | ||
// function resulted in a value that needs to be further matched | ||
return matchEntry(source, parent, root, matcherResult, errorCollector); | ||
return matchEntry(source, parent, root, matcherResult, failureLog); | ||
} | ||
@@ -239,3 +279,3 @@ | ||
// source ia a plain object, can be partially matched | ||
return matchPlainObj(source, parent, root, matcher as any, errorCollector); | ||
return matchPlainObj(source, parent, root, matcher as any, failureLog); | ||
} | ||
@@ -245,3 +285,3 @@ | ||
// source is an array | ||
return matchArr(source, root, matcher as any, errorCollector); | ||
return matchArr(source, parent, root, matcher as any, failureLog); | ||
} | ||
@@ -251,3 +291,3 @@ | ||
errorCollector?.push( | ||
failureLog?.push( | ||
`value ${JSON.stringify( | ||
@@ -266,5 +306,6 @@ source | ||
source: T, | ||
parent: P, | ||
root: R, | ||
matcher: Match.Arr<T, C, P, R>, | ||
errorCollector: Match.ErrorCollector | ||
failureLog?: Match.FailureLog | ||
): boolean { | ||
@@ -278,3 +319,3 @@ if (Array.isArray(matcher)) { | ||
errorCollector?.push( | ||
failureLog?.push( | ||
`array lengths are not equal: value length ${source.length} !== matcher length ${matcher.length}` | ||
@@ -289,6 +330,8 @@ ); | ||
while (++index < length) { | ||
if (!Object.is(source[index], matcher[index])) { | ||
if ( | ||
!matchEntry(source[index], source, root, matcher[index], failureLog) | ||
) { | ||
// item did not match, return false | ||
errorCollector?.push( | ||
failureLog?.push( | ||
`index ${index} does not match with value ${JSON.stringify( | ||
@@ -307,2 +350,77 @@ source[index] | ||
// matcher is plain object | ||
if (`every` in matcher) { | ||
return matchCompound( | ||
source, | ||
parent, | ||
root, | ||
['every', ...(matcher.every as any)], | ||
failureLog | ||
); | ||
} | ||
if (`some` in matcher) { | ||
return matchCompound( | ||
source, | ||
parent, | ||
root, | ||
['some', ...(matcher.some as any)], | ||
failureLog | ||
); | ||
} | ||
if (`none` in matcher) { | ||
return matchCompound( | ||
source, | ||
parent, | ||
root, | ||
['none', ...(matcher.none as any)], | ||
failureLog | ||
); | ||
} | ||
if (`single` in matcher) { | ||
return matchCompound( | ||
source, | ||
parent, | ||
root, | ||
['single', ...(matcher.single as any)], | ||
failureLog | ||
); | ||
} | ||
if (`someItem` in matcher) { | ||
return matchTraversal( | ||
source, | ||
root, | ||
'someItem', | ||
matcher.someItem as any, | ||
failureLog | ||
); | ||
} | ||
if (`everyItem` in matcher) { | ||
return matchTraversal( | ||
source, | ||
root, | ||
'everyItem', | ||
matcher.everyItem as any, | ||
failureLog | ||
); | ||
} | ||
if (`noneItem` in matcher) { | ||
return matchTraversal( | ||
source, | ||
root, | ||
'noneItem', | ||
matcher.noneItem as any, | ||
failureLog | ||
); | ||
} | ||
if (`singleItem` in matcher) { | ||
return matchTraversal( | ||
source, | ||
root, | ||
'singleItem', | ||
matcher.singleItem as any, | ||
failureLog | ||
); | ||
} | ||
// matcher is plain object with index keys | ||
@@ -316,3 +434,3 @@ | ||
errorCollector?.push( | ||
failureLog?.push( | ||
`index ${index} does not exist in source ${JSON.stringify( | ||
@@ -332,3 +450,3 @@ source | ||
matcherAtIndex, | ||
errorCollector | ||
failureLog | ||
); | ||
@@ -339,3 +457,3 @@ | ||
errorCollector?.push( | ||
failureLog?.push( | ||
`index ${index} does not match with value ${JSON.stringify( | ||
@@ -363,7 +481,7 @@ (source as any)[index] | ||
matcher: Match.Obj<T, C, P, R>, | ||
errorCollector: Match.ErrorCollector | ||
failureLog?: Match.FailureLog | ||
): boolean { | ||
if (Array.isArray(matcher)) { | ||
// the matcher is of compound type | ||
return matchCompound(source, parent, root, matcher as any, errorCollector); | ||
return matchCompound(source, parent, root, matcher as any, failureLog); | ||
} | ||
@@ -377,3 +495,3 @@ | ||
errorCollector?.push( | ||
failureLog?.push( | ||
`key ${key} is specified in matcher but not present in value ${JSON.stringify( | ||
@@ -393,7 +511,7 @@ source | ||
matcher[key], | ||
errorCollector | ||
failureLog | ||
); | ||
if (!result) { | ||
errorCollector?.push( | ||
failureLog?.push( | ||
`key ${key} does not match in value ${JSON.stringify( | ||
@@ -420,3 +538,3 @@ (source as any)[key] | ||
compound: [Match.CompoundType, ...Match.Entry<T, C, P, R>[]], | ||
errorCollector: string[] | undefined | ||
failureLog?: Match.FailureLog | ||
): boolean { | ||
@@ -442,7 +560,7 @@ // first item indicates compound match type | ||
compound[index] as Entry, | ||
errorCollector | ||
failureLog | ||
); | ||
if (!result) { | ||
errorCollector?.push( | ||
failureLog?.push( | ||
`in compound "every": match at index ${index} failed` | ||
@@ -465,7 +583,7 @@ ); | ||
compound[index] as Entry, | ||
errorCollector | ||
failureLog | ||
); | ||
if (result) { | ||
errorCollector?.push( | ||
failureLog?.push( | ||
`in compound "none": match at index ${index} succeeded` | ||
@@ -490,3 +608,3 @@ ); | ||
compound[index] as Entry, | ||
errorCollector | ||
failureLog | ||
); | ||
@@ -496,3 +614,3 @@ | ||
if (onePassed) { | ||
errorCollector?.push( | ||
failureLog?.push( | ||
`in compound "single": multiple matches succeeded` | ||
@@ -509,3 +627,3 @@ ); | ||
if (!onePassed) { | ||
errorCollector?.push(`in compound "single": no matches succeeded`); | ||
failureLog?.push(`in compound "single": no matches succeeded`); | ||
} | ||
@@ -523,3 +641,3 @@ | ||
compound[index] as Entry, | ||
errorCollector | ||
failureLog | ||
); | ||
@@ -532,3 +650,3 @@ | ||
errorCollector?.push(`in compound "some": no matches succeeded`); | ||
failureLog?.push(`in compound "some": no matches succeeded`); | ||
@@ -539,1 +657,79 @@ return false; | ||
} | ||
function matchTraversal<T extends any[], C extends any[], R>( | ||
source: T, | ||
root: R, | ||
matchType: Match.ArrayTraversalType, | ||
matcher: Match.Entry<T[keyof T], C[keyof C], T, R>, | ||
failureLog?: Match.FailureLog | ||
): boolean { | ||
let index = -1; | ||
const length = source.length; | ||
switch (matchType) { | ||
case 'someItem': { | ||
while (++index < length) { | ||
if (matchEntry(source[index], source, root, matcher, failureLog)) { | ||
return true; | ||
} | ||
} | ||
failureLog?.push( | ||
`in array traversal "someItem": no items matched given matcher` | ||
); | ||
return false; | ||
} | ||
case 'everyItem': { | ||
while (++index < length) { | ||
if (!matchEntry(source[index], source, root, matcher, failureLog)) { | ||
failureLog?.push( | ||
`in array traversal "everyItem": at least one item did not match given matcher` | ||
); | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
case 'noneItem': { | ||
while (++index < length) { | ||
if (matchEntry(source[index], source, root, matcher, failureLog)) { | ||
failureLog?.push( | ||
`in array traversal "noneItem": at least one item matched given matcher` | ||
); | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
case 'singleItem': { | ||
let singleMatched = false; | ||
while (++index < length) { | ||
if (matchEntry(source[index], source, root, matcher, failureLog)) { | ||
if (singleMatched) { | ||
failureLog?.push( | ||
`in array traversal "singleItem": more than one item matched given matcher` | ||
); | ||
return false; | ||
} | ||
singleMatched = true; | ||
} | ||
} | ||
if (!singleMatched) { | ||
failureLog?.push( | ||
`in array traversal "singleItem": no item matched given matcher` | ||
); | ||
return false; | ||
} | ||
return true; | ||
} | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
220504
4706