Skip to main content

lemma/evaluation/
response.rs

1use crate::evaluation::operations::{OperationRecord, OperationResult};
2use indexmap::IndexMap;
3use serde::Serialize;
4
5/// Facts from a specific document
6#[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/// Response from evaluating a Lemma document
18#[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/// Result of evaluating a single rule
26#[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    /// Computed type of this rule's result
37    /// Every rule MUST have a type (Lemma is strictly typed)
38    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}