mon-entreprise/publicodes/source/rule.tsx

157 lines
4.2 KiB
TypeScript

import { filter, mapObjIndexed, pick } from 'ramda'
import { ASTNode, EvaluatedNode } from './AST/types'
import { bonus, makeJsx, mergeMissing } from './evaluation'
import { registerEvaluationFunction } from './evaluationFunctions'
import parse, { mecanismKeys } from './parse'
import { Context } from './parsePublicodes'
import { ReferenceNode } from './reference'
import {
parseRendNonApplicable,
parseReplacements,
ReplacementNode,
} from './replacement'
import { nameLeaf, ruleParents } from './ruleUtils'
import { capitalise0 } from './utils'
export type Rule = {
formule?: Record<string, unknown> | string
question?: string
description?: string
unité?: string
acronyme?: string
exemples?: any
nom: string
résumé?: string
icônes?: string
titre?: string
cotisation?: {
branche: string
}
type?: string
note?: string
remplace?: RendNonApplicable | Array<RendNonApplicable>
'rend non applicable'?: Remplace | Array<string>
suggestions?: Record<string, string | number | Record<string, unknown>>
références?: { [source: string]: string }
API?: string
}
type Remplace =
| {
règle: string
par?: Record<string, unknown> | string | number
dans?: Array<string> | string
'sauf dans'?: Array<string> | string
}
| string
type RendNonApplicable = Exclude<Remplace, { par: any }>
export type RuleNode = {
dottedName: string
title: string
nodeKind: 'rule'
jsx: any
virtualRule: boolean
rawNode: Rule
replacements: Array<ReplacementNode>
explanation: {
parent: ASTNode | false
valeur: ASTNode
}
suggestions: Record<string, ASTNode>
}
export default function parseRule(
rawRule: Rule,
context: Context
): ReferenceNode {
const dottedName = [context.dottedName, rawRule.nom]
.filter(Boolean)
.join(' . ')
if (context.parsedRules[dottedName]) {
throw new Error(`La référence '${dottedName}' a déjà été définie`)
}
const ruleValue = {
...pick(mecanismKeys, rawRule),
...('formule' in rawRule && { valeur: rawRule.formule }),
'nom dans la situation': dottedName,
}
const ruleContext = { ...context, dottedName }
let name = nameLeaf(dottedName)
if (context.dottedName) {
name = `${nameLeaf(context.dottedName)} (${name})`
}
const [parent] = ruleParents(dottedName)
const explanation = {
valeur: parse(ruleValue, ruleContext),
parent: !!parent && parse(parent, context),
}
context.parsedRules[dottedName] = filter(Boolean, {
dottedName,
replacements: [
...parseRendNonApplicable(rawRule['rend non applicable'], ruleContext),
...parseReplacements(rawRule.remplace, ruleContext),
],
title: capitalise0(rawRule['titre'] || name),
suggestions: mapObjIndexed(
(node) => parse(node, ruleContext),
rawRule.suggestions ?? {}
),
nodeKind: 'rule',
jsx: (node) => (
<>
<code className="ui__ light-bg">{capitalise0(node.rawNode.nom)}</code>
&nbsp;
{makeJsx(node.explanation.valeur)}
</>
),
explanation,
rawNode: rawRule,
virtualRule: !!context.dottedName,
}) as RuleNode
// We return the parsedReference
return parse(rawRule.nom, context) as ReferenceNode
}
registerEvaluationFunction('rule', function evaluate(node) {
if (this.cache[node.dottedName]) {
return this.cache[node.dottedName]
}
const explanation = { ...node.explanation }
this.cache._meta.contextRule.push(node.dottedName)
this.cache._meta.parentEvaluationStack ??= []
let parent: EvaluatedNode | null = null
if (
explanation.parent &&
!this.cache._meta.parentEvaluationStack.includes(node.dottedName)
) {
this.cache._meta.parentEvaluationStack.push(node.dottedName)
parent = this.evaluateNode(explanation.parent) as EvaluatedNode
explanation.parent = parent
this.cache._meta.parentEvaluationStack.pop()
}
let valeur: EvaluatedNode | null = null
if (!parent || parent.nodeValue !== false) {
valeur = this.evaluateNode(explanation.valeur) as EvaluatedNode
explanation.valeur = valeur
}
const evaluation = {
...node,
explanation,
nodeValue: valeur && 'nodeValue' in valeur ? valeur.nodeValue : false,
missingVariables: mergeMissing(
valeur?.missingVariables,
bonus(parent?.missingVariables)
),
...(valeur && 'unit' in valeur && { unit: valeur.unit }),
}
this.cache._meta.contextRule.pop()
this.cache[node.dottedName] = evaluation
return evaluation
})