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