@surface/path-matcher
Version:
Provides path matching capabilities.
250 lines (249 loc) • 10.3 kB
JavaScript
/* eslint-disable complexity */
/* eslint-disable max-statements */
function createExtendedRange(min, max, minQuantifier = 1, maxQuantifier = 0) {
return createRange(min, max) + createRange(0, 9, minQuantifier, maxQuantifier);
}
function createLeadingZeros(size) {
switch (size) {
case 0:
return "";
case 1:
return "0";
case 2:
return "00";
default:
return `0{${size}}`;
}
}
function createRange(min, max, minQuantifier = 1, maxQuantifier = 0) {
if (minQuantifier + maxQuantifier == 0) {
return "";
}
const range = min == max
? String(max)
: min == 0 && max == 9
? "\\d"
: `[${min}${max - min > 1 ? "-" : ""}${max}]`;
const quantifier = minQuantifier < maxQuantifier
? `{${minQuantifier},${maxQuantifier}}`
: minQuantifier > 1
? `{${minQuantifier}}`
: "";
return range + quantifier;
}
function getRangeInfo(start, end) {
const startString = start.toString().replace("-", "");
const endString = end.toString().replace("-", "");
let intersection = null;
if (startString.length == endString.length) {
intersection = -1;
for (let index = 0; index < endString.length; index++) {
if (startString[index] != endString[index]) {
break;
}
intersection = index;
}
}
const startValue = start.toString().replace("-", "");
const endValue = end.toString().replace("-", "");
return {
start: {
ceiling: startValue[0] + createLeadingZeros(startValue.length - 1),
value: startValue,
},
end: {
ceiling: endValue[0] + createLeadingZeros(endValue.length - 1),
value: endValue,
},
sign: end < 0 ? "-" : "",
intersection,
};
}
function parseRange(start, end, minLength) {
const patterns = [];
const range = getRangeInfo(start, end);
const leading = createLeadingZeros(Math.max(minLength - range.end.value.length, 0));
for (let i = range.end.value.length - 1; i > -1; i--) {
if (i - 1 == range.intersection) {
break;
}
const digit = Number(range.end.value[i]);
if (digit == 0 && i == range.end.value.length - 1) {
patterns.push(range.sign + leading + range.end.value);
}
else if (i > 0 && digit > 0 || i == 0 && digit > 1) {
const rest = range.end.value.length - i - 1;
const startRange = i == 0 ? 1 : 0;
const endRange = digit - (i == range.end.value.length - 1 ? 0 : 1);
const pattern = range.sign + leading + range.end.value.substring(0, i) + createExtendedRange(startRange, endRange, rest);
if (patterns.length > 0) {
patterns.push("|");
}
patterns.push(pattern);
}
}
if (start == 0) {
const rest = range.end.value.length - 1;
if (rest > 1) {
if (minLength == 1) {
patterns.push("|", range.sign + createExtendedRange(1, 9, 1, rest - 1));
}
else {
for (let index = rest - 1; index > 0; index--) {
const leading = createLeadingZeros(minLength - index - 1);
patterns.push("|", range.sign + leading + createExtendedRange(1, 9, index));
}
}
}
if (range.sign) {
patterns.push("|", createLeadingZeros(minLength - 1) + range.sign + createRange(1, 9));
patterns.push("|", createLeadingZeros(minLength));
}
else {
patterns.push("|", createLeadingZeros(minLength - 1) + createRange(0, 9));
}
}
else if (range.start.value == range.start.ceiling && range.intersection == -1) {
patterns.push("|", range.sign + createExtendedRange(Number(range.start.value[0]), Number(range.end.value[0]) - 1, range.start.value.length - 1));
}
else {
const rest = range.end.value.length - range.start.value.length;
if (rest > 1) {
if (minLength == 1) {
patterns.push("|", range.sign + createExtendedRange(1, 9, range.start.value.length, range.end.value.length - 2));
}
else {
for (let index = range.end.value.length - 2; index > range.start.value.length - 1; index--) {
const leading = createLeadingZeros(Math.max(minLength - index - 1, 0));
patterns.push("|", range.sign + leading + createExtendedRange(1, 9, index));
}
}
}
const leading = createLeadingZeros(Math.max(minLength - range.start.value.length, 0));
for (let i = range.start.value.length - 1; i > -1; i--) {
const startDigit = Number(range.start.value[i]);
const endDigit = Number(range.end.value[i]);
const hasIntersected = i == range.intersection;
const willIntersect = i - 1 == range.intersection;
if (hasIntersected || willIntersect && endDigit - startDigit < 2) {
break;
}
const isTrailingDigit = i == range.start.value.length - 1;
const canResolveZero = startDigit == 0 && (willIntersect || i == 1 && !isTrailingDigit);
const canResolveNine = startDigit == 9 && range.start.value[i + 1] == "0";
if (startDigit == 9 && isTrailingDigit) {
if (patterns.length > 0) {
patterns.push("|");
}
patterns.push(range.sign + leading + range.start.value);
}
else if (canResolveZero || canResolveNine || startDigit > 0 && startDigit < 9) {
const rest = range.start.value.length - i - 1;
const startOffset = isTrailingDigit || range.start.value[i + 1] == "0" && (i > 0 || range.start.value.length == 2) ? 0 : 1;
const endOffset = isTrailingDigit ? 0 : 1;
const startRange = startDigit + startOffset;
const endRange = startDigit < 9 && willIntersect ? endDigit - endOffset : 9;
const pattern = range.sign + leading + range.start.value.substring(0, i) + createExtendedRange(startRange, endRange, rest);
if (patterns.length > 0) {
patterns.push("|");
}
patterns.push(pattern);
}
}
}
return patterns.join("");
}
function parseSteppedRange(start, end, multiplier, minLength) {
const positives = [];
const negatives = [];
for (let i = start; i <= end; i += multiplier) {
const patterns = i >= 0 ? positives : negatives;
if (patterns.length > 0) {
patterns.push("|");
}
const value = Math.abs(i).toString();
if (value == "0") {
patterns.push(createLeadingZeros(minLength));
}
else {
patterns.push(createLeadingZeros(Math.max(minLength - value.length, 0)) + value);
}
}
if (negatives.length > 0) {
return ["-(?:", ...negatives, ")", "|", ...positives].join("");
}
return negatives.concat(positives).join("");
}
export function parseAlphaRange(startChar, endChar, multiplier) {
const tokens = [];
const startRange = startChar.charCodeAt(0);
const endRange = endChar.charCodeAt(0);
if (multiplier != 0) {
tokens.push("[");
const isValidRange = startRange < endRange
&& (startChar == startChar.toLowerCase() && endChar == endChar.toLowerCase()
|| startChar == startChar.toUpperCase() && endChar == endChar.toUpperCase());
if (multiplier == 1 && isValidRange) {
tokens.push(`${String.fromCharCode(startRange)}-${String.fromCharCode(endRange)}`);
}
else {
for (let i = startRange; i <= endRange; i += multiplier) {
const char = String.fromCharCode(i);
if (char != "\\") {
if (char == "]" || char == "^") {
tokens.push("\\");
}
tokens.push(char);
}
}
}
tokens.push("]");
}
return tokens.join("");
}
export function parseNumericRange(startRange, endRange, multiplier) {
const tokens = [];
const getMinLength = (value) => {
const abs = value.replace("-", "");
return abs.startsWith("0") ? abs.length : 1;
};
const minLength = Math.max(getMinLength(startRange), getMinLength(endRange));
const parsedStartRange = Number(startRange);
const parsedEndRange = Number(endRange);
const start = Math.min(parsedStartRange, parsedEndRange);
const end = Math.max(parsedStartRange, parsedEndRange);
const inSimpleRange = (value) => value >= 0 && value <= 9;
if (multiplier == 1) {
if (inSimpleRange(start) && inSimpleRange(end)) {
if (minLength > 1) {
tokens.push(createLeadingZeros(minLength - 1));
}
tokens.push("[");
tokens.push(`${start.toString()}-${end.toString()}`);
tokens.push("]");
}
else if (start >= 0 && end > 0) {
tokens.push(parseRange(start, end, minLength));
}
else if (start < 0 && end <= 0) {
tokens.push(parseRange(end, start, minLength));
}
else {
tokens.push(parseRange(0, start, minLength));
tokens.push("|");
tokens.push(parseRange(1, end, minLength));
}
}
else if (inSimpleRange(start) && inSimpleRange(end)) {
tokens.push(createLeadingZeros(minLength - 1));
tokens.push("[");
for (let i = start; i <= end; i += multiplier) {
tokens.push(i.toString());
}
tokens.push("]");
}
else {
tokens.push(parseSteppedRange(start, end, multiplier, minLength));
}
return tokens.join("");
}