1pub mod explanation;
8pub mod expression;
9pub mod operations;
10pub mod response;
11
12use crate::evaluation::explanation::{ExplanationNode, ValueSource};
13use crate::evaluation::response::EvaluatedRule;
14use crate::planning::semantics::{Expression, Fact, FactPath, FactValue, LiteralValue, RulePath};
15use crate::planning::ExecutionPlan;
16use indexmap::IndexMap;
17pub use operations::{ComputationKind, OperationKind, OperationRecord, OperationResult};
18pub use response::{Facts, Response, RuleResult};
19use std::collections::HashMap;
20
21pub(crate) struct EvaluationContext {
23 fact_values: HashMap<FactPath, LiteralValue>,
24 pub(crate) rule_results: HashMap<RulePath, OperationResult>,
25 rule_explanations: HashMap<RulePath, crate::evaluation::explanation::Explanation>,
26 operations: Option<Vec<crate::OperationRecord>>,
27 pub(crate) sources: HashMap<String, String>,
28 explanation_nodes: HashMap<usize, crate::evaluation::explanation::ExplanationNode>,
29 now: LiteralValue,
30}
31
32impl EvaluationContext {
33 fn new(plan: &ExecutionPlan, now: LiteralValue, record_operations: bool) -> Self {
34 let fact_values: HashMap<FactPath, LiteralValue> = plan
35 .facts
36 .iter()
37 .filter_map(|(path, d)| d.value().map(|v| (path.clone(), v.clone())))
38 .collect();
39 Self {
40 fact_values,
41 rule_results: HashMap::new(),
42 rule_explanations: HashMap::new(),
43 operations: if record_operations {
44 Some(Vec::new())
45 } else {
46 None
47 },
48 sources: plan.sources.clone(),
49 explanation_nodes: HashMap::new(),
50 now,
51 }
52 }
53
54 pub(crate) fn now(&self) -> &LiteralValue {
55 &self.now
56 }
57
58 fn get_fact(&self, fact_path: &FactPath) -> Option<&LiteralValue> {
59 self.fact_values.get(fact_path)
60 }
61
62 fn push_operation(&mut self, kind: OperationKind) {
63 if let Some(ref mut ops) = self.operations {
64 ops.push(OperationRecord { kind });
65 }
66 }
67
68 fn set_explanation_node(
69 &mut self,
70 expression: &Expression,
71 node: crate::evaluation::explanation::ExplanationNode,
72 ) {
73 self.explanation_nodes
74 .insert(expression as *const Expression as usize, node);
75 }
76
77 fn get_explanation_node(
78 &self,
79 expression: &Expression,
80 ) -> Option<&crate::evaluation::explanation::ExplanationNode> {
81 self.explanation_nodes
82 .get(&(expression as *const Expression as usize))
83 }
84
85 fn get_rule_explanation(
86 &self,
87 rule_path: &RulePath,
88 ) -> Option<&crate::evaluation::explanation::Explanation> {
89 self.rule_explanations.get(rule_path)
90 }
91
92 fn set_rule_explanation(
93 &mut self,
94 rule_path: RulePath,
95 explanation: crate::evaluation::explanation::Explanation,
96 ) {
97 self.rule_explanations.insert(rule_path, explanation);
98 }
99}
100
101fn collect_used_facts_from_explanation(
102 node: &ExplanationNode,
103 out: &mut HashMap<FactPath, LiteralValue>,
104) {
105 match node {
106 ExplanationNode::Value {
107 value,
108 source: ValueSource::Fact { fact_ref },
109 ..
110 } => {
111 out.entry(fact_ref.clone()).or_insert_with(|| value.clone());
112 }
113 ExplanationNode::Value { .. } => {}
114 ExplanationNode::RuleReference { expansion, .. } => {
115 collect_used_facts_from_explanation(expansion.as_ref(), out);
116 }
117 ExplanationNode::Computation { operands, .. } => {
118 for op in operands {
119 collect_used_facts_from_explanation(op, out);
120 }
121 }
122 ExplanationNode::Branches {
123 matched,
124 non_matched,
125 ..
126 } => {
127 if let Some(ref cond) = matched.condition {
128 collect_used_facts_from_explanation(cond, out);
129 }
130 collect_used_facts_from_explanation(&matched.result, out);
131 for nm in non_matched {
132 collect_used_facts_from_explanation(&nm.condition, out);
133 if let Some(ref res) = nm.result {
134 collect_used_facts_from_explanation(res, out);
135 }
136 }
137 }
138 ExplanationNode::Condition { operands, .. } => {
139 for op in operands {
140 collect_used_facts_from_explanation(op, out);
141 }
142 }
143 ExplanationNode::Veto { .. } => {}
144 }
145}
146
147#[derive(Default)]
149pub(crate) struct Evaluator;
150
151impl Evaluator {
152 pub(crate) fn evaluate(
165 &self,
166 plan: &ExecutionPlan,
167 now: LiteralValue,
168 record_operations: bool,
169 ) -> Response {
170 let mut context = EvaluationContext::new(plan, now, record_operations);
171
172 let mut response = Response {
173 spec_name: plan.spec_name.clone(),
174 spec_hash: None,
175 spec_effective_from: None,
176 spec_effective_to: None,
177 facts: Vec::new(),
178 results: IndexMap::new(),
179 };
180
181 for exec_rule in &plan.rules {
183 if let Some(ref mut ops) = context.operations {
184 ops.clear();
185 }
186 context.explanation_nodes.clear();
187
188 let (result, explanation) = expression::evaluate_rule(exec_rule, &mut context);
189
190 context
191 .rule_results
192 .insert(exec_rule.path.clone(), result.clone());
193 context.set_rule_explanation(exec_rule.path.clone(), explanation.clone());
194
195 let rule_operations = context.operations.clone().unwrap_or_default();
196
197 if !exec_rule.path.segments.is_empty() {
198 continue;
199 }
200
201 let unless_branches: Vec<(Option<Expression>, Expression)> = exec_rule.branches[1..]
202 .iter()
203 .map(|b| (b.condition.clone(), b.result.clone()))
204 .collect();
205
206 response.add_result(RuleResult {
207 rule: EvaluatedRule {
208 name: exec_rule.name.clone(),
209 path: exec_rule.path.clone(),
210 default_expression: exec_rule.branches[0].result.clone(),
211 unless_branches,
212 source_location: exec_rule.source.clone(),
213 rule_type: exec_rule.rule_type.clone(),
214 },
215 result,
216 facts: vec![],
217 operations: rule_operations,
218 explanation: Some(explanation),
219 rule_type: exec_rule.rule_type.clone(),
220 });
221 }
222
223 let mut used_facts: HashMap<FactPath, LiteralValue> = HashMap::new();
224 for rule_result in response.results.values() {
225 if let Some(ref explanation) = rule_result.explanation {
226 collect_used_facts_from_explanation(explanation.tree.as_ref(), &mut used_facts);
227 }
228 }
229
230 let fact_list: Vec<Fact> = plan
232 .facts
233 .keys()
234 .filter_map(|path| {
235 used_facts.remove(path).map(|value| Fact {
236 path: path.clone(),
237 value: FactValue::Literal(value),
238 source: None,
239 })
240 })
241 .collect();
242
243 if !fact_list.is_empty() {
244 response.facts = vec![Facts {
245 fact_path: String::new(),
246 referencing_fact_name: String::new(),
247 facts: fact_list,
248 }];
249 }
250
251 response
252 }
253}