1use crate::evaluation::operations::{OperationRecord, OperationResult};
2use crate::planning::semantics::{Expression, Fact, LemmaType, RulePath, Source};
3use indexmap::IndexMap;
4use serde::Serialize;
5
6#[derive(Debug, Clone, Serialize)]
9pub struct EvaluatedRule {
10 pub name: String,
11 pub path: RulePath,
12 pub default_expression: Expression,
13 pub unless_branches: Vec<(Option<Expression>, Expression)>,
14 pub source_location: Source,
15 pub rule_type: LemmaType,
16}
17
18#[derive(Debug, Clone, Serialize)]
20pub struct Facts {
21 pub fact_path: String,
22 pub referencing_fact_name: String,
23 pub facts: Vec<Fact>,
24}
25
26#[derive(Debug, Clone, Serialize)]
28pub struct Response {
29 pub spec_name: String,
30 pub facts: Vec<Facts>,
31 pub results: IndexMap<String, RuleResult>,
32}
33
34#[derive(Debug, Clone, Serialize)]
36pub struct RuleResult {
37 #[serde(skip_serializing)]
38 pub rule: EvaluatedRule,
39 pub result: OperationResult,
40 pub facts: Vec<Fact>,
41 #[serde(skip_serializing)]
42 pub operations: Vec<OperationRecord>,
43 #[serde(skip_serializing_if = "Option::is_none")]
44 pub proof: Option<crate::evaluation::proof::Proof>,
45 pub rule_type: LemmaType,
47}
48
49impl Response {
50 pub fn add_result(&mut self, result: RuleResult) {
51 self.results.insert(result.rule.name.clone(), result);
52 }
53
54 pub fn filter_rules(&mut self, rule_names: &[String]) {
55 self.results.retain(|name, _| rule_names.contains(name));
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62 use crate::planning::semantics::{
63 primitive_boolean, primitive_number, Expression, ExpressionKind, LemmaType, LiteralValue,
64 RulePath, Span,
65 };
66 use rust_decimal::Decimal;
67 use std::str::FromStr;
68
69 fn dummy_source() -> Source {
70 Source::new(
71 "test",
72 Span {
73 start: 0,
74 end: 0,
75 line: 1,
76 col: 1,
77 },
78 "test_spec",
79 std::sync::Arc::from("spec test_spec\nfact x: 1\nrule result: x"),
80 )
81 }
82
83 fn dummy_evaluated_rule(name: &str) -> EvaluatedRule {
84 EvaluatedRule {
85 name: name.to_string(),
86 path: RulePath::new(vec![], name.to_string()),
87 default_expression: Expression::new(
88 ExpressionKind::Literal(Box::new(LiteralValue::from_bool(true))),
89 dummy_source(),
90 ),
91 unless_branches: vec![],
92 source_location: dummy_source(),
93 rule_type: primitive_number().clone(),
94 }
95 }
96
97 #[test]
98 fn test_response_serialization() {
99 let mut results = IndexMap::new();
100 results.insert(
101 "test_rule".to_string(),
102 RuleResult {
103 rule: dummy_evaluated_rule("test_rule"),
104 result: OperationResult::Value(Box::new(LiteralValue::number(
105 Decimal::from_str("42").unwrap(),
106 ))),
107 facts: vec![],
108 operations: vec![],
109 proof: None,
110 rule_type: primitive_number().clone(),
111 },
112 );
113 let response = Response {
114 spec_name: "test_spec".to_string(),
115 facts: vec![],
116 results,
117 };
118
119 let json = serde_json::to_string(&response).unwrap();
120 assert!(json.contains("test_spec"));
121 assert!(json.contains("test_rule"));
122 assert!(json.contains("results"));
123 }
124
125 #[test]
126 fn test_response_filter_rules() {
127 let mut results = IndexMap::new();
128 results.insert(
129 "rule1".to_string(),
130 RuleResult {
131 rule: dummy_evaluated_rule("rule1"),
132 result: OperationResult::Value(Box::new(LiteralValue::from_bool(true))),
133 facts: vec![],
134 operations: vec![],
135 proof: None,
136 rule_type: primitive_boolean().clone(),
137 },
138 );
139 results.insert(
140 "rule2".to_string(),
141 RuleResult {
142 rule: dummy_evaluated_rule("rule2"),
143 result: OperationResult::Value(Box::new(LiteralValue::from_bool(false))),
144 facts: vec![],
145 operations: vec![],
146 proof: None,
147 rule_type: primitive_boolean().clone(),
148 },
149 );
150 let mut response = Response {
151 spec_name: "test_spec".to_string(),
152 facts: vec![],
153 results,
154 };
155
156 response.filter_rules(&["rule1".to_string()]);
157
158 assert_eq!(response.results.len(), 1);
159 assert_eq!(response.results.values().next().unwrap().rule.name, "rule1");
160 }
161
162 #[test]
163 fn test_rule_result_types() {
164 let success = RuleResult {
165 rule: dummy_evaluated_rule("rule1"),
166 result: OperationResult::Value(Box::new(LiteralValue::from_bool(true))),
167 facts: vec![],
168 operations: vec![],
169 proof: None,
170 rule_type: primitive_boolean().clone(),
171 };
172 assert!(matches!(success.result, OperationResult::Value(_)));
173
174 let missing = RuleResult {
175 rule: dummy_evaluated_rule("rule3"),
176 result: OperationResult::Veto(Some("Missing fact: fact1".to_string())),
177 facts: vec![crate::planning::semantics::Fact {
178 path: crate::planning::semantics::FactPath::new(vec![], "fact1".to_string()),
179 value: crate::planning::semantics::FactValue::Literal(
180 crate::planning::semantics::LiteralValue::from_bool(false),
181 ),
182 source: None,
183 }],
184 operations: vec![],
185 proof: None,
186 rule_type: LemmaType::veto_type(),
187 };
188 assert_eq!(missing.facts.len(), 1);
189 assert_eq!(missing.facts[0].path.fact, "fact1");
190 assert!(matches!(missing.result, OperationResult::Veto(_)));
191
192 let veto = RuleResult {
193 rule: dummy_evaluated_rule("rule4"),
194 result: OperationResult::Veto(Some("Vetoed".to_string())),
195 facts: vec![],
196 operations: vec![],
197 proof: None,
198 rule_type: LemmaType::veto_type(),
199 };
200 assert_eq!(
201 veto.result,
202 OperationResult::Veto(Some("Vetoed".to_string()))
203 );
204 }
205}