lemma/
response.rs

1use crate::LiteralValue;
2use serde::Serialize;
3use std::collections::HashMap;
4
5/// Response from evaluating a Lemma document
6///
7/// Contains the results of evaluating all rules in a document,
8/// including their computed values and any variable bindings.
9#[derive(Debug, Clone, Serialize)]
10pub struct Response {
11    pub doc_name: String,
12    pub results: Vec<RuleResult>,
13    pub warnings: Vec<String>,
14}
15
16/// A record of a single operation during evaluation
17///
18/// Represents one operation performed during rule evaluation,
19/// capturing the actual values and decisions made during execution.
20#[derive(Debug, Clone, Serialize)]
21#[serde(tag = "type", rename_all = "snake_case")]
22pub enum OperationRecord {
23    FactUsed {
24        name: String,
25        value: LiteralValue,
26    },
27    RuleUsed {
28        name: String,
29        value: LiteralValue,
30    },
31    OperationExecuted {
32        operation: String,
33        inputs: Vec<LiteralValue>,
34        result: LiteralValue,
35        unless_clause_index: Option<usize>,
36    },
37    UnlessClauseEvaluated {
38        index: usize,
39        matched: bool,
40        result_if_matched: Option<LiteralValue>,
41    },
42    DefaultValue {
43        value: LiteralValue,
44    },
45    FinalResult {
46        value: LiteralValue,
47    },
48}
49
50/// Result of evaluating a single rule
51///
52/// Represents the outcome of evaluating one rule, including
53/// whether it matched, what value it produced, and any variable bindings.
54#[derive(Debug, Clone, Serialize)]
55pub struct RuleResult {
56    pub rule_name: String,
57    pub result: Option<LiteralValue>,
58    pub bindings: HashMap<String, LiteralValue>,
59    pub missing_facts: Option<Vec<String>>,
60    pub veto_message: Option<String>,
61    pub operations: Vec<OperationRecord>,
62}
63
64impl Response {
65    pub fn new(doc_name: String) -> Self {
66        Self {
67            doc_name,
68            results: Vec::new(),
69            warnings: Vec::new(),
70        }
71    }
72
73    pub fn add_result(&mut self, result: RuleResult) {
74        self.results.push(result);
75    }
76
77    pub fn add_warning(&mut self, warning: String) {
78        self.warnings.push(warning);
79    }
80
81    /// Filter results to only include specified rules
82    ///
83    /// Keeps only the rules whose names are in the provided list.
84    /// This is used when evaluating specific rules (e.g., `doc:rule1,rule2`)
85    pub fn filter_rules(&mut self, rule_names: &[String]) {
86        self.results.retain(|r| rule_names.contains(&r.rule_name));
87    }
88}
89
90impl RuleResult {
91    pub fn success(
92        rule_name: String,
93        result: LiteralValue,
94        bindings: HashMap<String, LiteralValue>,
95    ) -> Self {
96        Self {
97            rule_name,
98            result: Some(result),
99            bindings,
100            missing_facts: None,
101            veto_message: None,
102            operations: Vec::new(),
103        }
104    }
105
106    pub fn success_with_operations(
107        rule_name: String,
108        result: LiteralValue,
109        bindings: HashMap<String, LiteralValue>,
110        operations: Vec<OperationRecord>,
111    ) -> Self {
112        Self {
113            rule_name,
114            result: Some(result),
115            bindings,
116            missing_facts: None,
117            veto_message: None,
118            operations,
119        }
120    }
121
122    pub fn no_match(rule_name: String) -> Self {
123        Self {
124            rule_name,
125            result: None,
126            bindings: HashMap::new(),
127            missing_facts: None,
128            veto_message: None,
129            operations: Vec::new(),
130        }
131    }
132
133    pub fn missing_facts(rule_name: String, facts: Vec<String>) -> Self {
134        Self {
135            rule_name,
136            result: None,
137            bindings: HashMap::new(),
138            missing_facts: Some(facts),
139            veto_message: None,
140            operations: Vec::new(),
141        }
142    }
143
144    pub fn veto(rule_name: String, message: Option<String>) -> Self {
145        Self {
146            rule_name,
147            result: None,
148            bindings: HashMap::new(),
149            missing_facts: None,
150            veto_message: message,
151            operations: Vec::new(),
152        }
153    }
154}