blob: 8ceead7c04907f77a9be74b2876798eabf2f4c06 [file] [log] [blame]
// Copyright 2017 The casbin Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the 'License');
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an 'AS IS' BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// escapeAssertion escapes the dots in the assertion,
// because the expression evaluation doesn't support such variable names.
import { mustGetDefaultFileSystem } from '../persist';
const escapeAssertionReg = new RegExp(/([()\s|&,=!><+\-*/]|^)((r|p)[0-9]*)\./g);
function escapeAssertion(s: string): string {
s = s.replace(escapeAssertionReg, (match) => {
// Replace only the last dot with underscore (preserve the prefix character)
const lastDotIdx = match.lastIndexOf('.');
if (lastDotIdx > 0) {
return match.substring(0, lastDotIdx) + '_';
}
return match;
});
return s;
}
// removeComments removes the comments starting with # in the text.
function removeComments(s: string): string {
const pos = s.indexOf('#');
return pos > -1 ? s.slice(0, pos).trim() : s;
}
// arrayEquals determines whether two string arrays are identical.
function arrayEquals(a: string[] = [], b: string[] = []): boolean {
const aLen = a.length;
const bLen = b.length;
if (aLen !== bLen) {
return false;
}
for (let i = 0; i < aLen; i++) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
}
// array2DEquals determines whether two 2-dimensional string arrays are identical.
function array2DEquals(a: string[][] = [], b: string[][] = []): boolean {
const aLen = a.length;
const bLen = a.length;
if (aLen !== bLen) {
return false;
}
for (let i = 0; i < aLen; i++) {
if (!arrayEquals(a[i], b[i])) {
return false;
}
}
return true;
}
// arrayRemoveDuplicates removes any duplicated elements in a string array.
function arrayRemoveDuplicates(s: string[]): string[] {
return [...new Set(s)];
}
// arrayToString gets a printable string for a string array.
function arrayToString(a: string[]): string {
return a.join(', ');
}
// paramsToString gets a printable string for variable number of parameters.
function paramsToString(...v: string[]): string {
return v.join(', ');
}
// setEquals determines whether two string sets are identical.
function setEquals(a: string[], b: string[]): boolean {
return arrayEquals(a.sort(), b.sort());
}
// readFile return a promise for readFile.
function readFile(path: string, encoding?: string): Promise<string> {
const fs = mustGetDefaultFileSystem();
return new Promise<string>((resolve, reject) => {
try {
const content = fs.readFileSync(path, encoding || 'utf8') as string;
resolve(content);
} catch (e) {
reject(e);
}
});
}
// writeFile return a promise for writeFile.
function writeFile(path: string, file: string, encoding?: string): Promise<void> {
const fs = mustGetDefaultFileSystem();
return new Promise<void>((resolve, reject) => {
try {
fs.writeFileSync(path, file, encoding || 'utf-8');
resolve();
} catch (e) {
reject(e);
}
});
}
const evalRegG = new RegExp(/\beval\(([^),]*)\)/g);
const evalReg = new RegExp(/\beval\(([^),]*)\)/);
// hasEval determine whether matcher contains function eval
function hasEval(s: string): boolean {
return evalReg.test(s);
}
// replaceEval replace function eval with the value of its parameters
function replaceEval(s: string, ruleName: string, rule: string): string {
return s.replace(`eval(${ruleName})`, '(' + rule + ')');
}
// getEvalValue returns the parameters of function eval
function getEvalValue(s: string): string[] {
const subMatch = s.match(evalRegG);
const rules: string[] = [];
if (!subMatch) {
return [];
}
for (const rule of subMatch) {
const index: number = rule.indexOf('(');
rules.push(rule.slice(index + 1, -1));
}
return rules;
}
// generatorRunSync handle generator function in Sync model and return value which is not Promise
function generatorRunSync(iterator: Generator<any>): any {
let { value, done } = iterator.next();
while (true) {
if (value instanceof Promise) {
throw new Error('cannot handle Promise in generatorRunSync, Please use generatorRunAsync');
}
if (!done) {
const temp = value;
({ value, done } = iterator.next(temp));
} else {
return value;
}
}
}
// generatorRunAsync handle generator function in Async model and return Promise
async function generatorRunAsync(iterator: Generator<any>): Promise<any> {
let { value, done } = iterator.next();
while (true) {
if (!done) {
const temp = await value;
({ value, done } = iterator.next(temp));
} else {
return value;
}
}
}
function deepCopy(obj: Array<any> | any): any {
if (typeof obj !== 'object') return;
const newObj: any = obj instanceof Array ? [] : {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
}
}
return newObj;
}
function customIn(a: number | string, b: number | string): number {
if ((b as any) instanceof Array) {
return (((b as any) as Array<any>).includes(a) as unknown) as number;
}
return ((a in (b as any)) as unknown) as number;
}
function bracketCompatible(exp: string): string {
// TODO: This function didn't support nested bracket.
if (!(exp.includes(' in ') && exp.includes(' ('))) {
return exp;
}
const re = / \([^)]*\)/g;
const array = exp.split('');
let reResult: RegExpExecArray | null;
while ((reResult = re.exec(exp)) !== null) {
if (!(reResult[0] as string).includes(',')) {
continue;
}
array[reResult.index + 1] = '[';
array[re.lastIndex - 1] = ']';
}
exp = array.join('');
return exp;
}
export {
escapeAssertion,
removeComments,
arrayEquals,
array2DEquals,
arrayRemoveDuplicates,
arrayToString,
paramsToString,
setEquals,
readFile,
writeFile,
hasEval,
replaceEval,
getEvalValue,
generatorRunSync,
generatorRunAsync,
deepCopy,
customIn,
bracketCompatible,
};