From 0464d0da18fddfff2e22b7136dcb39f1c70e7ffa Mon Sep 17 00:00:00 2001 From: Stefano Del Prete Date: Mon, 13 Jan 2025 20:38:29 +0100 Subject: [PATCH] feat: conditional modifiers for variables (#662) * should be signed * remove boolean modifiers that cna be replaced by istrue/isfalse * added exists check on string, removed isfalse check on boolean since it the same as istrue but inverting the true/false strings --- src/lib/parser/index.ts | 246 +++++++++++++++++++++++++++++++++++----- 1 file changed, 218 insertions(+), 28 deletions(-) diff --git a/src/lib/parser/index.ts b/src/lib/parser/index.ts index 294a67d7..ef92cb3a 100755 --- a/src/lib/parser/index.ts +++ b/src/lib/parser/index.ts @@ -50,7 +50,7 @@ export function parseString(str: string, value: ParseValue) { }; const re = - /\{(?file|url|user|debug|link|metricsUser|metricsZipline)\.(?\w+)(::(?\w+))?(::(?\S+))?\}/gi; + /\{(?file|url|user|debug|link|metricsUser|metricsZipline)\.(?\w+)(::(?(\w+|<|<=|=|>=|>|\^|\$|~|\/)+))?((::(?\S+))|(?\[(?".*")\|\|(?".*")\]))?\}/gi; let matches: RegExpMatchArray | null; while ((matches = re.exec(str))) { @@ -59,6 +59,7 @@ export function parseString(str: string, value: ParseValue) { const index = matches.index as number; const getV = value[matches.groups.type as keyof ParseValue]; + if (!getV) { str = replaceCharsFromString(str, '{unknown_type}', index, re.lastIndex); re.lastIndex = index; @@ -94,7 +95,14 @@ export function parseString(str: string, value: ParseValue) { if (matches.groups.mod) { str = replaceCharsFromString( str, - modifier(matches.groups.mod, v, matches.groups.mod_tzlocale ?? undefined), + modifier( + matches.groups.mod, + v, + matches.groups.mod_tzlocale ?? undefined, + matches.groups.mod_check_true ?? undefined, + matches.groups.mod_check_false ?? undefined, + value, + ), index, re.lastIndex, ); @@ -109,8 +117,17 @@ export function parseString(str: string, value: ParseValue) { return str; } -function modifier(mod: string, value: unknown, tzlocale?: string): string { +function modifier( + mod: string, + value: unknown, + tzlocale?: string, + check_true?: string, + check_false?: string, + _value?: ParseValue, +): string { mod = mod.toLowerCase(); + check_true = check_true?.slice(1, -1); + check_false = check_false?.slice(1, -1); if (value instanceof Date) { const args: [string?, { timeZone: string }?] = [undefined, undefined]; @@ -173,58 +190,231 @@ function modifier(mod: string, value: unknown, tzlocale?: string): string { return `{unknown_date_modifier(${mod})}`; } } else if (typeof value === 'string') { - switch (mod) { - case 'upper': + switch (true) { + case mod == 'upper': return value.toUpperCase(); - case 'lower': + case mod == 'lower': return value.toLowerCase(); - case 'title': + case mod == 'title': return value.charAt(0).toUpperCase() + value.slice(1); - case 'length': + case mod == 'length': return value.length.toString(); - case 'reverse': + case mod == 'reverse': return value.split('').reverse().join(''); - case 'base64': + case mod == 'base64': return btoa(value); - case 'hex': + case mod == 'hex': return toHex(value); - case 'string': + case mod == 'string': return value; + case mod.startsWith('exists'): { + if (typeof check_true !== 'string' || typeof check_false !== 'string') + return `{unknown_str_modifier(${mod})}`; + + if (_value) { + return value + ? parseString(check_true, _value) || check_true + : parseString(check_false, _value) || check_false; + } + + return value ? check_true : check_false; + } + case mod.startsWith('='): { + if (typeof check_true !== 'string' || typeof check_false !== 'string') + return `{unknown_str_modifier(${mod})}`; + + const check = mod.replace('=', ''); + + if (!check) return `{unknown_str_modifier(${mod})}`; + + if (_value) { + return value.toLowerCase() == check + ? parseString(check_true, _value) || check_true + : parseString(check_false, _value) || check_false; + } + + return value.toLowerCase() == check ? check_true : check_false; + } + case mod.startsWith('$'): { + if (typeof check_true !== 'string' || typeof check_false !== 'string') + return `{unknown_str_modifier(${mod})}`; + + const check = mod.replace('$', ''); + + if (!check) return `{unknown_str_modifier(${mod})}`; + + if (_value) { + return value.toLowerCase().startsWith(check) + ? parseString(check_true, _value) || check_true + : parseString(check_false, _value) || check_false; + } + + return value.toLowerCase().startsWith(check) ? check_true : check_false; + } + case mod.startsWith('^'): { + if (typeof check_true !== 'string' || typeof check_false !== 'string') + return `{unknown_str_modifier(${mod})}`; + + const check = mod.replace('^', ''); + + if (!check) return `{unknown_str_modifier(${mod})}`; + + if (_value) { + return value.toLowerCase().endsWith(check) + ? parseString(check_true, _value) || check_true + : parseString(check_false, _value) || check_false; + } + + return value.toLowerCase().endsWith(check) ? check_true : check_false; + } + case mod.startsWith('~'): { + if (typeof check_true !== 'string' || typeof check_false !== 'string') + return `{unknown_str_modifier(${mod})}`; + + const check = mod.replace('~', ''); + + if (!check) return `{unknown_str_modifier(${mod})}`; + + if (_value) { + return value.toLowerCase().includes(check) + ? parseString(check_true, _value) || check_true + : parseString(check_false, _value) || check_false; + } + + return value.toLowerCase().includes(check) ? check_true : check_false; + } default: return `{unknown_str_modifier(${mod})}`; } } else if (typeof value === 'number') { - switch (mod) { - case 'comma': + switch (true) { + case mod == 'comma': return value.toLocaleString(); - case 'hex': + case mod == 'hex': return value.toString(16); - case 'octal': + case mod == 'octal': return value.toString(8); - case 'binary': + case mod == 'binary': return value.toString(2); - case 'bytes': + case mod == 'bytes': return bytes(value); - case 'string': + case mod == 'string': return value.toString(); + case mod.startsWith('>='): { + if (typeof check_true !== 'string' || typeof check_false !== 'string') + return `{unknown_int_modifier(${mod})}`; + + const check = Number(mod.replace('>=', '')); + + if (Number.isNaN(check)) return `{unknown_int_modifier(${mod})}`; + + if (_value) { + return value >= check + ? parseString(check_true, _value) || check_true + : parseString(check_false, _value) || check_false; + } + + return value >= check ? check_true : check_false; + } + case mod.startsWith('>'): { + if (typeof check_true !== 'string' || typeof check_false !== 'string') + return `{unknown_int_modifier(${mod})}`; + + const check = Number(mod.replace('>', '')); + + if (Number.isNaN(check)) return `{unknown_int_modifier(${mod})}`; + + if (_value) { + return value > check + ? parseString(check_true, _value) || check_true + : parseString(check_false, _value) || check_false; + } + + return value > check ? check_true : check_false; + } + case mod.startsWith('='): { + if (typeof check_true !== 'string' || typeof check_false !== 'string') + return `{unknown_int_modifier(${mod})}`; + + const check = Number(mod.replace('=', '')); + + if (Number.isNaN(check)) return `{unknown_int_modifier(${mod})}`; + + if (_value) { + return value == check + ? parseString(check_true, _value) || check_true + : parseString(check_false, _value) || check_false; + } + + return value == check ? check_true : check_false; + } + case mod.startsWith('<='): { + if (typeof check_true !== 'string' || typeof check_false !== 'string') + return `{unknown_int_modifier(${mod})}`; + + const check = Number(mod.replace('<=', '')); + + if (Number.isNaN(check)) return `{unknown_int_modifier(${mod})}`; + + if (_value) { + return value <= check + ? parseString(check_true, _value) || check_true + : parseString(check_false, _value) || check_false; + } + + return value <= check ? check_true : check_false; + } + case mod.startsWith('<'): { + if (typeof check_true !== 'string' || typeof check_false !== 'string') + return `{unknown_int_modifier(${mod})}`; + + const check = Number(mod.replace('<', '')); + + if (Number.isNaN(check)) return `{unknown_int_modifier(${mod})}`; + + if (_value) { + return value < check + ? parseString(check_true, _value) || check_true + : parseString(check_false, _value) || check_false; + } + + return value < check ? check_true : check_false; + } default: return `{unknown_int_modifier(${mod})}`; } } else if (typeof value === 'boolean') { - switch (mod) { - case 'yesno': - return value ? 'Yes' : 'No'; - case 'onoff': - return value ? 'On' : 'Off'; - case 'truefalse': - return value ? 'True' : 'False'; - case 'string': - return value ? 'true' : 'false'; + switch (true) { + case mod == 'istrue': { + if (typeof check_true !== 'string' || typeof check_false !== 'string') + return `{unknown_bool_modifier(${mod})}`; + + if (_value) { + return value + ? parseString(check_true, _value) || check_true + : parseString(check_false, _value) || check_false; + } + + return value ? check_true : check_false; + } default: return `{unknown_bool_modifier(${mod})}`; } } + if ( + typeof check_false == 'string' && + ['>', '>=', '=', '<=', '<', '~', '$', '^'].some((modif) => mod.startsWith(modif)) + ) { + if (_value) return parseString(check_false, _value) || check_false; + return check_false; + } + + if (typeof check_false == 'string' && ['istrue', 'isfalse', 'exists'].includes(mod)) { + if (_value) return parseString(check_false, _value) || check_false; + return check_false; + } + return `{unknown_modifier(${mod})}`; }