(ruleIf: string | undefined, envs: {[key: string]: string})
| 227 | } |
| 228 | |
| 229 | static evaluateRuleIf (ruleIf: string | undefined, envs: {[key: string]: string}): boolean { |
| 230 | if (ruleIf === undefined) return true; |
| 231 | assert(!/\$\{\w+\}/.test(ruleIf), chalk`rules:rule if invalid expression syntax: {blueBright ${ruleIf}}\nuse {green $VAR} not {red \${VAR\}} in rules:if`); |
| 232 | let evalStr = ruleIf; |
| 233 | |
| 234 | const flagsToBinary = (flags: string): number => { |
| 235 | let binary = 0; |
| 236 | if (flags.includes("i")) { |
| 237 | binary |= RE2JS.CASE_INSENSITIVE; |
| 238 | } |
| 239 | if (flags.includes("s")) { |
| 240 | binary |= RE2JS.DOTALL; |
| 241 | } |
| 242 | if (flags.includes("m")) { |
| 243 | binary |= RE2JS.MULTILINE; |
| 244 | } |
| 245 | return binary; |
| 246 | }; |
| 247 | |
| 248 | // Expand all variables |
| 249 | evalStr = this.expandTextWith(evalStr, { |
| 250 | unescape: JSON.stringify("$"), |
| 251 | variable: (name) => JSON.stringify(envs[name] ?? null).replaceAll("\\\\", "\\"), |
| 252 | }); |
| 253 | const expandedEvalStr = evalStr; |
| 254 | |
| 255 | // Scenario when RHS is a <regex> |
| 256 | // https://regexr.com/85sjo |
| 257 | const pattern1 = /\s*(?<operator>(?:=~)|(?:!~))\s*\/(?<rhs>.*?[^\\])\/(?<flags>[igmsuy]*)(\s|$|\))/g; |
| 258 | evalStr = evalStr.replaceAll(pattern1, (_, operator, rhs, flags, remainingTokens) => { |
| 259 | let _operator; |
| 260 | switch (operator) { |
| 261 | case "=~": |
| 262 | _operator = "!="; |
| 263 | break; |
| 264 | case "!~": |
| 265 | _operator = "=="; |
| 266 | break; |
| 267 | default: |
| 268 | throw operator; |
| 269 | } |
| 270 | const _rhs = JSON.stringify(rhs); // JSON.stringify for escaping `"` |
| 271 | const containsNonEscapedSlash = /(?<!\\)\//.test(_rhs); |
| 272 | const assertMsg = [ |
| 273 | "Error attempting to evaluate the following rules:", |
| 274 | " rules:", |
| 275 | ` - if: '${expandedEvalStr}'`, |
| 276 | "as rhs contains unescaped quote", |
| 277 | ]; |
| 278 | assert(!containsNonEscapedSlash, assertMsg.join("\n")); |
| 279 | const flagsBinary = flagsToBinary(flags); |
| 280 | return `.matchRE2JS(RE2JS.compile(${_rhs}, ${flagsBinary})) ${_operator} null${remainingTokens}`; |
| 281 | }); |
| 282 | |
| 283 | // Scenario when RHS is surrounded by single/double-quotes |
| 284 | // https://regexr.com/85t0g |
| 285 | const pattern2 = /\s*(?<operator>=~|!~)\s*(["'])(?<rhs>(?:\\.|[^\\])*?)\2/g; |
| 286 | evalStr = evalStr.replaceAll(pattern2, (_, operator, __, rhs) => { |
no test coverage detected