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