1pub mod expression;
8pub mod operations;
9pub mod proof;
10pub mod response;
11
12use crate::evaluation::response::EvaluatedRule;
13use crate::planning::semantics::{Expression, Fact, FactPath, FactValue, LiteralValue, RulePath};
14use crate::planning::ExecutionPlan;
15use indexmap::IndexMap;
16pub use operations::{ComputationKind, OperationKind, OperationRecord, OperationResult};
17pub use response::{Facts, Response, RuleResult};
18use std::collections::HashMap;
19
20pub(crate) struct EvaluationContext {
22 fact_values: HashMap<FactPath, LiteralValue>,
23 pub(crate) rule_results: HashMap<RulePath, OperationResult>,
24 rule_proofs: HashMap<RulePath, crate::evaluation::proof::Proof>,
25 operations: Vec<crate::OperationRecord>,
26 pub(crate) sources: HashMap<String, String>,
27 proof_nodes: HashMap<Expression, crate::evaluation::proof::ProofNode>,
28 now: LiteralValue,
30}
31
32impl EvaluationContext {
33 fn new(plan: &ExecutionPlan, now: LiteralValue) -> 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_proofs: HashMap::new(),
43 operations: Vec::new(),
44 sources: plan.sources.clone(),
45 proof_nodes: HashMap::new(),
46 now,
47 }
48 }
49
50 pub(crate) fn now(&self) -> &LiteralValue {
51 &self.now
52 }
53
54 fn get_fact(&self, fact_path: &FactPath) -> Option<&LiteralValue> {
55 self.fact_values.get(fact_path)
56 }
57
58 fn push_operation(&mut self, kind: OperationKind) {
59 self.operations.push(OperationRecord { kind });
60 }
61
62 fn set_proof_node(
63 &mut self,
64 expression: &Expression,
65 node: crate::evaluation::proof::ProofNode,
66 ) {
67 self.proof_nodes.insert(expression.clone(), node);
68 }
69
70 fn get_proof_node(
71 &self,
72 expression: &Expression,
73 ) -> Option<&crate::evaluation::proof::ProofNode> {
74 self.proof_nodes.get(expression)
75 }
76
77 fn get_rule_proof(&self, rule_path: &RulePath) -> Option<&crate::evaluation::proof::Proof> {
78 self.rule_proofs.get(rule_path)
79 }
80
81 fn set_rule_proof(&mut self, rule_path: RulePath, proof: crate::evaluation::proof::Proof) {
82 self.rule_proofs.insert(rule_path, proof);
83 }
84}
85
86#[derive(Default)]
88pub(crate) struct Evaluator;
89
90impl Evaluator {
91 pub(crate) fn evaluate(&self, plan: &ExecutionPlan, now: LiteralValue) -> Response {
100 let mut context = EvaluationContext::new(plan, now);
101
102 let mut response = Response {
103 spec_name: plan.spec_name.clone(),
104 facts: Vec::new(),
105 results: IndexMap::new(),
106 };
107
108 let mut used_facts: HashMap<FactPath, LiteralValue> = HashMap::new();
109
110 for exec_rule in &plan.rules {
112 context.operations.clear();
113 context.proof_nodes.clear();
114
115 let (result, proof) = expression::evaluate_rule(exec_rule, &mut context);
116
117 context
118 .rule_results
119 .insert(exec_rule.path.clone(), result.clone());
120 context.set_rule_proof(exec_rule.path.clone(), proof.clone());
121
122 let rule_operations = context.operations.clone();
123
124 for op in &rule_operations {
125 if let OperationKind::FactUsed { fact_ref, value } = &op.kind {
126 used_facts.entry(fact_ref.clone()).or_insert(value.clone());
127 }
128 }
129
130 if !exec_rule.path.segments.is_empty() {
131 continue;
132 }
133
134 let unless_branches: Vec<(Option<Expression>, Expression)> = exec_rule.branches[1..]
135 .iter()
136 .map(|b| (b.condition.clone(), b.result.clone()))
137 .collect();
138
139 response.add_result(RuleResult {
140 rule: EvaluatedRule {
141 name: exec_rule.name.clone(),
142 path: exec_rule.path.clone(),
143 default_expression: exec_rule.branches[0].result.clone(),
144 unless_branches,
145 source_location: exec_rule.source.clone(),
146 rule_type: exec_rule.rule_type.clone(),
147 },
148 result,
149 facts: vec![],
150 operations: rule_operations,
151 proof: Some(proof),
152 rule_type: exec_rule.rule_type.clone(),
153 });
154 }
155
156 let fact_list: Vec<Fact> = plan
158 .facts
159 .keys()
160 .filter_map(|path| {
161 used_facts.remove(path).map(|value| Fact {
162 path: path.clone(),
163 value: FactValue::Literal(value),
164 source: None,
165 })
166 })
167 .collect();
168
169 if !fact_list.is_empty() {
170 response.facts = vec![Facts {
171 fact_path: String::new(),
172 referencing_fact_name: String::new(),
173 facts: fact_list,
174 }];
175 }
176
177 response
178 }
179}