/* eslint-disable @typescript-eslint/ban-types */ import { compose, map, mapObjIndexed } from 'ramda' import { ASTNode, EvaluatedNode, NodeKind } from './AST/types' import { evaluationFunctions } from './evaluationFunctions' import { simplifyNodeUnit } from './nodeUnits' import parse from './parse' import parsePublicodes, { disambiguateReference } from './parsePublicodes' import { getReplacements, inlineReplacements, ReplacementNode, } from './replacement' import { Rule, RuleNode } from './rule' import * as utils from './ruleUtils' const emptyCache = () => ({ _meta: { contextRule: [] }, }) type Cache = { _meta: { contextRule: Array parentEvaluationStack?: Array inversionFail?: | { given: string estimated: string } | true inRecalcul?: boolean filter?: string } } export type EvaluationOptions = Partial<{ unit: string }> export * as cyclesLib from './AST/graph' export { reduceAST, updateAST } from './AST' export * from './components' export { formatValue } from './format' export { default as translateRules } from './translateRules' export { ASTNode, EvaluatedNode } export { Unit, Evaluation } from './AST/types' export { parsePublicodes } export { utils } export { Rule } export type evaluationFunction = ( this: Engine, node: ASTNode & { nodeKind: Kind } ) => ASTNode & { nodeKind: Kind } & EvaluatedNode export type ParsedRules = Record< Name, RuleNode & { dottedName: Name } > export default class Engine { parsedRules: ParsedRules parsedSituation: Record = {} replacements: Record> = {} cache: Cache private warnings: Array = [] constructor(rules: string | Record | ParsedRules) { this.cache = emptyCache() this.resetCache() if (typeof rules === 'string') { this.parsedRules = parsePublicodes(rules) as ParsedRules } const firstRuleObject = Object.values(rules)[0] as Rule | RuleNode if ( typeof firstRuleObject === 'object' && firstRuleObject != null && 'nodeKind' in firstRuleObject ) { this.parsedRules = rules as ParsedRules return } this.parsedRules = parsePublicodes( rules as Record ) as ParsedRules this.replacements = getReplacements(this.parsedRules) } private resetCache() { this.cache = emptyCache() } setSituation( situation: Partial> = {} ) { this.resetCache() this.parsedSituation = mapObjIndexed((value, key) => { if (value && typeof value === 'object' && 'nodeKind' in value) { return value as ASTNode } return compose( inlineReplacements(this.replacements), disambiguateReference(this.parsedRules) )( parse(value, { dottedName: `situation [${key}]`, parsedRules: {}, }) ) }, situation) return this } evaluate(expression: string | Object): EvaluatedNode { /* TODO EN ATTENDANT d'AVOIR une meilleure gestion d'erreur, on va mocker console.warn */ const originalWarn = console.warn console.warn = (warning: string) => { this.warnings.push(warning) originalWarn(warning) } const result = this.evaluateNode( compose( inlineReplacements(this.replacements), disambiguateReference(this.parsedRules) )( parse(expression, { dottedName: "evaluation'''", parsedRules: {}, }) ) ) console.warn = originalWarn return result } getWarnings() { return this.warnings } inversionFail(): boolean { return !!this.cache._meta.inversionFail } getParsedRules(): ParsedRules { return this.parsedRules } evaluateNode(node: N): N & EvaluatedNode { if (!node.nodeKind) { throw Error('The provided node must have a "nodeKind" attribute') } else if (!evaluationFunctions[node.nodeKind]) { throw Error(`Unknown "nodeKind": ${node.nodeKind}`) } return evaluationFunctions[node.nodeKind].call(this, node) } } // This function is an util for allowing smother migration to the new Engine API export function evaluateRule( engine: Engine, dottedName: DottedName, modifiers: Object = {} ): EvaluatedRule { const evaluation = simplifyNodeUnit( engine.evaluate({ valeur: dottedName, ...modifiers }) ) const rule = engine.getParsedRules()[dottedName] as RuleNode & { dottedName: DottedName } return { ...rule.rawNode, ...rule, ...evaluation, } as EvaluatedRule } export type EvaluatedRule = EvaluatedNode & Omit< (ASTNode & { nodeKind: 'rule' }) & (ASTNode & { nodeKind: 'rule' })['rawNode'] & { dottedName: Name }, 'nodeKind' >