1use crate::evaluation::operations::{OperationRecord, OperationResult};
2use indexmap::IndexMap;
3use serde::Serialize;
4
5#[derive(Debug, Clone, Serialize)]
7pub struct Facts {
8 pub fact_path: String,
9 pub referencing_fact_name: String,
10 #[serde(skip_serializing_if = "Option::is_none")]
11 pub document_reference: Option<String>,
12 pub facts: Vec<crate::LemmaFact>,
13 #[serde(skip_serializing_if = "Vec::is_empty", default)]
14 pub referenced_docs: Vec<Facts>,
15}
16
17#[derive(Debug, Clone, Serialize)]
19pub struct Response {
20 pub doc_name: String,
21 pub facts: Vec<Facts>,
22 pub results: IndexMap<String, RuleResult>,
23}
24
25#[derive(Debug, Clone, Serialize)]
27pub struct RuleResult {
28 #[serde(skip_serializing)]
29 pub rule: crate::LemmaRule,
30 pub result: OperationResult,
31 pub facts: Vec<crate::LemmaFact>,
32 #[serde(skip_serializing)]
33 pub operations: Vec<OperationRecord>,
34 #[serde(skip_serializing_if = "Option::is_none")]
35 pub proof: Option<crate::evaluation::proof::Proof>,
36 pub rule_type: crate::LemmaType,
39}
40
41impl Response {
42 pub fn add_result(&mut self, result: RuleResult) {
43 self.results.insert(result.rule.name.clone(), result);
44 }
45
46 pub fn filter_rules(&mut self, rule_names: &[String]) {
47 self.results.retain(|name, _| rule_names.contains(name));
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54 use crate::{Expression, ExpressionKind, LemmaRule, LiteralValue, OperationResult};
55 use rust_decimal::Decimal;
56 use std::str::FromStr;
57
58 fn dummy_rule(name: &str) -> LemmaRule {
59 LemmaRule {
60 name: name.to_string(),
61 expression: Expression {
62 kind: ExpressionKind::Literal(LiteralValue::boolean(crate::BooleanValue::True)),
63 source_location: None,
64 },
65 unless_clauses: vec![],
66 source_location: None,
67 }
68 }
69
70 #[test]
71 fn test_response_serialization() {
72 let mut results = IndexMap::new();
73 results.insert(
74 "test_rule".to_string(),
75 RuleResult {
76 rule: dummy_rule("test_rule"),
77 result: OperationResult::Value(LiteralValue::number(
78 Decimal::from_str("42").unwrap(),
79 )),
80 facts: vec![],
81 operations: vec![],
82 proof: None,
83 rule_type: crate::semantic::standard_number().clone(),
84 },
85 );
86 let response = Response {
87 doc_name: "test_doc".to_string(),
88 facts: vec![],
89 results,
90 };
91
92 let json = serde_json::to_string(&response).unwrap();
93 assert!(json.contains("test_doc"));
94 assert!(json.contains("test_rule"));
95 assert!(json.contains("results"));
96 }
97
98 #[test]
99 fn test_response_filter_rules() {
100 let mut results = IndexMap::new();
101 results.insert(
102 "rule1".to_string(),
103 RuleResult {
104 rule: dummy_rule("rule1"),
105 result: OperationResult::Value(LiteralValue::boolean(crate::BooleanValue::True)),
106 facts: vec![],
107 operations: vec![],
108 proof: None,
109 rule_type: crate::semantic::standard_boolean().clone(),
110 },
111 );
112 results.insert(
113 "rule2".to_string(),
114 RuleResult {
115 rule: dummy_rule("rule2"),
116 result: OperationResult::Value(LiteralValue::boolean(crate::BooleanValue::False)),
117 facts: vec![],
118 operations: vec![],
119 proof: None,
120 rule_type: crate::semantic::standard_boolean().clone(),
121 },
122 );
123 let mut response = Response {
124 doc_name: "test_doc".to_string(),
125 facts: vec![],
126 results,
127 };
128
129 response.filter_rules(&["rule1".to_string()]);
130
131 assert_eq!(response.results.len(), 1);
132 assert_eq!(response.results.values().next().unwrap().rule.name, "rule1");
133 }
134
135 #[test]
136 fn test_rule_result_types() {
137 let success = RuleResult {
138 rule: dummy_rule("rule1"),
139 result: OperationResult::Value(LiteralValue::boolean(crate::BooleanValue::True)),
140 facts: vec![],
141 operations: vec![],
142 proof: None,
143 rule_type: crate::semantic::standard_boolean().clone(),
144 };
145 assert!(matches!(success.result, OperationResult::Value(_)));
146
147 let missing = RuleResult {
148 rule: dummy_rule("rule3"),
149 result: OperationResult::Veto(Some("Missing fact: fact1".to_string())),
150 facts: vec![crate::LemmaFact {
151 reference: crate::FactReference::from_path(vec!["fact1".to_string()]),
152 value: crate::FactValue::TypeDeclaration {
153 base: "number".to_string(),
154 overrides: None,
155 from: None,
156 },
157 source_location: None,
158 }],
159 operations: vec![],
160 proof: None,
161 rule_type: crate::LemmaType::veto_type(),
162 };
163 assert_eq!(missing.facts.len(), 1);
164 assert_eq!(missing.facts[0].reference.to_string(), "fact1");
165 assert!(matches!(
166 missing.facts[0].value,
167 crate::FactValue::TypeDeclaration { .. }
168 ));
169 assert!(matches!(missing.result, OperationResult::Veto(_)));
170
171 let veto = RuleResult {
172 rule: dummy_rule("rule4"),
173 result: OperationResult::Veto(Some("Vetoed".to_string())),
174 facts: vec![],
175 operations: vec![],
176 proof: None,
177 rule_type: crate::LemmaType::veto_type(),
178 };
179 assert_eq!(
180 veto.result,
181 OperationResult::Veto(Some("Vetoed".to_string()))
182 );
183 }
184}