@finnair/path
Version:
Simple object path as array of strings and numbers
121 lines (120 loc) • 3.36 kB
JavaScript
import { Path } from './Path.js';
export function isPathExpression(component) {
return !!component && typeof component.test === 'function' && typeof component.find === 'function';
}
export class IndexMatcher {
index;
allowGaps = true;
constructor(index) {
this.index = index;
Path.validateIndex(index);
}
find(current, callback) {
if (Array.isArray(current) && this.index < current.length) {
return callback(current[this.index], this.index);
}
return true;
}
test(component) {
return component === this.index;
}
toString() {
return Path.indexToString(this.index);
}
}
export class PropertyMatcher {
property;
allowGaps = false;
constructor(property) {
this.property = property;
Path.validateProperty(property);
}
find(current, callback) {
if (typeof current === 'object' && current.hasOwnProperty(this.property)) {
return callback(current[this.property], this.property);
}
return true;
}
test(component) {
return String(component) === this.property;
}
toString() {
return Path.propertyToString(this.property);
}
}
export class UnionMatcher {
components;
constructor(components) {
this.components = components;
if (components.length < 2) {
throw new Error('Expected at least 2 properties');
}
components.forEach(Path.validateComponent);
}
find(current, callback) {
if (typeof current === 'object') {
for (const component of this.components) {
if (current.hasOwnProperty(component)) {
if (!callback(current[component], component)) {
return false;
}
}
}
}
return true;
}
test(component) {
const str = String(component);
return this.components.find(component => String(component) === str) !== undefined;
}
get allowGaps() {
return this.components.some(component => typeof component === 'number');
}
toString() {
return `[${this.components.map(this.propertyToString).join(',')}]`;
}
static of(...components) {
return new UnionMatcher(components);
}
propertyToString(property) {
return JSON.stringify(property);
}
}
export const AnyIndex = {
allowGaps: false,
find: (current, callback) => {
if (Array.isArray(current)) {
for (let i = 0; i < current.length; i++) {
if (!callback(current[i], i)) {
return false;
}
}
}
return true;
},
test: (component) => {
return typeof component === 'number' && Number.isInteger(component) && component >= 0;
},
toString: () => {
return '[*]';
},
};
export const AnyProperty = {
allowGaps: false,
find: (current, callback) => {
if (typeof current === 'object') {
for (let key in current) {
if (!callback(current[key], key)) {
return false;
}
}
}
return true;
},
test: () => {
return true;
},
toString: () => {
return '.*';
},
};