lemma/evaluation/
response.rs

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