@finnair/path
Version:
Simple object path as array of strings and numbers
158 lines (157 loc) • 5.91 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnionMatcher = exports.IndexMatcher = exports.PropertyMatcher = exports.AnyProperty = exports.AnyIndex = exports.PathMatcher = void 0;
const Path_js_1 = require("./Path.js");
const matchers_js_1 = require("./matchers.js");
Object.defineProperty(exports, "PropertyMatcher", { enumerable: true, get: function () { return matchers_js_1.PropertyMatcher; } });
Object.defineProperty(exports, "IndexMatcher", { enumerable: true, get: function () { return matchers_js_1.IndexMatcher; } });
Object.defineProperty(exports, "AnyIndex", { enumerable: true, get: function () { return matchers_js_1.AnyIndex; } });
Object.defineProperty(exports, "AnyProperty", { enumerable: true, get: function () { return matchers_js_1.AnyProperty; } });
Object.defineProperty(exports, "UnionMatcher", { enumerable: true, get: function () { return matchers_js_1.UnionMatcher; } });
class PathMatcher {
expressions;
allowGaps;
constructor(expressions) {
this.expressions = expressions;
this.allowGaps = expressions.some((expression) => expression.allowGaps);
Object.freeze(this.expressions);
Object.freeze(this);
}
find(root, collector) {
if (this.expressions.length === 0) {
collector(Path_js_1.Path.ROOT, root);
}
if (typeof root !== 'object') {
return;
}
const currentPath = [];
const handlers = [];
for (let i = 0; i < this.expressions.length - 1; i++) {
handlers[i] = intermediateHandler(i, this.expressions);
}
handlers[this.expressions.length - 1] = resultHandler();
this.expressions[0].find(root, handlers[0]);
function intermediateHandler(index, expressions) {
return (value, component) => {
currentPath[index] = component;
return expressions[index + 1].find(value, handlers[index + 1]);
};
}
function resultHandler() {
return (value, component) => {
return collector(Path_js_1.Path.of(...currentPath, component), value);
};
}
}
findAll(root, acceptUndefined) {
const results = [];
this.find(root, (path, value) => {
if (value !== undefined || acceptUndefined) {
results.push({ path, value });
return true;
}
return true;
});
return results;
}
findFirst(root, acceptUndefined) {
let result = undefined;
this.find(root, (path, value) => {
if (value !== undefined || acceptUndefined) {
result = { path, value };
return false;
}
return true;
});
return result;
}
findValues(root, acceptUndefined) {
const results = [];
this.find(root, (path, value) => {
if (value !== undefined || acceptUndefined) {
results.push(value);
return true;
}
return true;
});
return results;
}
findFirstValue(root, acceptUndefined) {
let result = undefined;
this.find(root, (path, value) => {
if (value !== undefined || acceptUndefined) {
result = value;
return false;
}
return true;
});
return result;
}
/**
* Exact match: path length must match the number of expressions and all expressions must match. Only sibling paths match.
*
* @param path
* @returns true if path is an exact match to expressions
*/
match(path) {
if (path.length !== this.expressions.length) {
return false;
}
for (let index = 0; index < this.expressions.length; index++) {
if (!this.expressions[index].test(path.componentAt(index))) {
return false;
}
}
return true;
}
/**
* Prefix match: path length must be equal or longer than the number of expressions and all expressions must match. All sibling and child paths match.
*
* @param path
* @returns true the start the path matches
*/
prefixMatch(path) {
if (path.length < this.expressions.length) {
return false;
}
for (let index = 0; index < this.expressions.length; index++) {
if (!this.expressions[index].test(path.componentAt(index))) {
return false;
}
}
return true;
}
/**
* Partial match: path length can be less than or more than the number of expressions, but all corresponding expressions must match. All parent, sibling and child paths match.
*
* @param path
* @returns true if all path components match
*/
partialMatch(path) {
for (let index = 0; index < this.expressions.length && index < path.length; index++) {
if (!this.expressions[index].test(path.componentAt(index))) {
return false;
}
}
return true;
}
toJSON() {
return this.expressions.reduce((str, expression) => str + expression.toString(), '$');
}
static of(...path) {
return new PathMatcher(path.map(component => {
const type = typeof component;
if (type === 'number') {
return new matchers_js_1.IndexMatcher(component);
}
if (type === 'string') {
return new matchers_js_1.PropertyMatcher(component);
}
if ((0, matchers_js_1.isPathExpression)(component)) {
return component;
}
throw new Error(`Unrecognized PathComponent: ${component} of type ${type}`);
}));
}
}
exports.PathMatcher = PathMatcher;