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 std::sync::Arc::from("spec test_spec\nfact x: 1\nrule result: x"),
79 )
80 }
81
82 fn dummy_evaluated_rule(name: &str) -> EvaluatedRule {
83 EvaluatedRule {
84 name: name.to_string(),
85 path: RulePath::new(vec![], name.to_string()),
86 default_expression: Expression::new(
87 ExpressionKind::Literal(Box::new(LiteralValue::from_bool(true))),
88 dummy_source(),
89 ),
90 unless_branches: vec![],
91 source_location: dummy_source(),
92 rule_type: primitive_number().clone(),
93 }
94 }
95
96 #[test]
97 fn test_response_serialization() {
98 let mut results = IndexMap::new();
99 results.insert(
100 "test_rule".to_string(),
101 RuleResult {
102 rule: dummy_evaluated_rule("test_rule"),
103 result: OperationResult::Value(Box::new(LiteralValue::number(
104 Decimal::from_str("42").unwrap(),
105 ))),
106 facts: vec![],
107 operations: vec![],
108 proof: None,
109 rule_type: primitive_number().clone(),
110 },
111 );
112 let response = Response {
113 spec_name: "test_spec".to_string(),
114 facts: vec![],
115 results,
116 };
117
118 let json = serde_json::to_string(&response).unwrap();
119 assert!(json.contains("test_spec"));
120 assert!(json.contains("test_rule"));
121 assert!(json.contains("results"));
122 }
123
124 #[test]
125 fn test_response_filter_rules() {
126 let mut results = IndexMap::new();
127 results.insert(
128 "rule1".to_string(),
129 RuleResult {
130 rule: dummy_evaluated_rule("rule1"),
131 result: OperationResult::Value(Box::new(LiteralValue::from_bool(true))),
132 facts: vec![],
133 operations: vec![],
134 proof: None,
135 rule_type: primitive_boolean().clone(),
136 },
137 );
138 results.insert(
139 "rule2".to_string(),
140 RuleResult {
141 rule: dummy_evaluated_rule("rule2"),
142 result: OperationResult::Value(Box::new(LiteralValue::from_bool(false))),
143 facts: vec![],
144 operations: vec![],
145 proof: None,
146 rule_type: primitive_boolean().clone(),
147 },
148 );
149 let mut response = Response {
150 spec_name: "test_spec".to_string(),
151 facts: vec![],
152 results,
153 };
154
155 response.filter_rules(&["rule1".to_string()]);
156
157 assert_eq!(response.results.len(), 1);
158 assert_eq!(response.results.values().next().unwrap().rule.name, "rule1");
159 }
160
161 #[test]
162 fn test_rule_result_types() {
163 let success = RuleResult {
164 rule: dummy_evaluated_rule("rule1"),
165 result: OperationResult::Value(Box::new(LiteralValue::from_bool(true))),
166 facts: vec![],
167 operations: vec![],
168 proof: None,
169 rule_type: primitive_boolean().clone(),
170 };
171 assert!(matches!(success.result, OperationResult::Value(_)));
172
173 let missing = RuleResult {
174 rule: dummy_evaluated_rule("rule3"),
175 result: OperationResult::Veto(Some("Missing fact: fact1".to_string())),
176 facts: vec![crate::planning::semantics::Fact {
177 path: crate::planning::semantics::FactPath::new(vec![], "fact1".to_string()),
178 value: crate::planning::semantics::FactValue::Literal(
179 crate::planning::semantics::LiteralValue::from_bool(false),
180 ),
181 source: None,
182 }],
183 operations: vec![],
184 proof: None,
185 rule_type: LemmaType::veto_type(),
186 };
187 assert_eq!(missing.facts.len(), 1);
188 assert_eq!(missing.facts[0].path.fact, "fact1");
189 assert!(matches!(missing.result, OperationResult::Veto(_)));
190
191 let veto = RuleResult {
192 rule: dummy_evaluated_rule("rule4"),
193 result: OperationResult::Veto(Some("Vetoed".to_string())),
194 facts: vec![],
195 operations: vec![],
196 proof: None,
197 rule_type: LemmaType::veto_type(),
198 };
199 assert_eq!(
200 veto.result,
201 OperationResult::Veto(Some("Vetoed".to_string()))
202 );
203 }
204}