You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

130 lines
4.2 KiB

/**
* ============================================================================
* JSON LOGIC EVALUATOR
* ============================================================================
*/
import type { JsonLogic } from '@/types';
/**
* Evaluador de expresiones JsonLogic
*/
export class JsonLogicEvaluator {
/**
* Evalúa una expresión JsonLogic
*/
evaluate(expression: JsonLogic, data: Record<string, any>): any {
// Primitivos
if (expression === null) return null;
if (typeof expression === 'boolean') return expression;
if (typeof expression === 'number') return expression;
if (typeof expression === 'string') return expression;
if (Array.isArray(expression)) {
return expression.map(item => this.evaluate(item, data));
}
if (typeof expression !== 'object') return expression;
// Operadores
const expr = expression as Record<string, any>;
const operator = Object.keys(expr)[0];
const args = expr[operator];
switch (operator) {
// Variables
case 'var':
return this.getVar(args, data);
// Lógicos
case '==':
case '===':
return this.evaluateAll(args, data).every((v, i, arr) =>
i === 0 || v === arr[0]
);
case '!=':
case '!==':
return this.evaluate(args[0], data) !== this.evaluate(args[1], data);
case '!':
return !this.evaluate(args, data);
case 'and':
return this.evaluateAll(args, data).every(Boolean);
case 'or':
return this.evaluateAll(args, data).some(Boolean);
// Comparación
case '>':
return this.evaluate(args[0], data) > this.evaluate(args[1], data);
case '>=':
return this.evaluate(args[0], data) >= this.evaluate(args[1], data);
case '<':
return this.evaluate(args[0], data) < this.evaluate(args[1], data);
case '<=':
return this.evaluate(args[0], data) <= this.evaluate(args[1], data);
// Condicionales
case 'if':
const [condition, thenBranch, elseBranch] = args;
return this.evaluate(condition, data)
? this.evaluate(thenBranch, data)
: this.evaluate(elseBranch !== undefined ? elseBranch : null, data);
// Arrays
case 'in':
const [item, array] = args;
const evaluatedItem = this.evaluate(item, data);
const evaluatedArray = this.evaluate(array, data);
return Array.isArray(evaluatedArray) && evaluatedArray.includes(evaluatedItem);
// Matemáticos
case '+':
return this.evaluateAll(args, data).reduce((a, b) => a + b, 0);
case '-':
const nums = this.evaluateAll(args, data);
return nums.length === 1 ? -nums[0] : nums[0] - nums[1];
case '*':
return this.evaluateAll(args, data).reduce((a, b) => a * b, 1);
case '/':
const [dividend, divisor] = this.evaluateAll(args, data);
return divisor !== 0 ? dividend / divisor : null;
case '%':
return this.evaluate(args[0], data) % this.evaluate(args[1], data);
default:
console.warn(`[JsonLogic] Unknown operator: ${operator}`);
return null;
}
}
private evaluateAll(args: any[], data: Record<string, any>): any[] {
return (Array.isArray(args) ? args : [args]).map(arg =>
this.evaluate(arg, data)
);
}
private getVar(path: string | any, data: Record<string, any>): any {
if (typeof path !== 'string') {
path = this.evaluate(path, data);
}
const parts = String(path).split('.');
let current: any = data;
for (const part of parts) {
if (current === undefined || current === null) return undefined;
current = current[part];
}
return current;
}
}

Powered by TurnKey Linux.