Skip to main content

lemma/evaluation/
operations.rs

1//! Operation types and result handling for evaluation
2
3use crate::planning::semantics::{
4    ArithmeticComputation, ComparisonComputation, FactPath, LiteralValue, LogicalComputation,
5    MathematicalComputation, RulePath,
6};
7use serde::{Deserialize, Serialize};
8
9/// Result of an operation (evaluating a rule or expression)
10#[derive(Debug, Clone, PartialEq, Serialize)]
11#[serde(rename_all = "snake_case")]
12pub enum OperationResult {
13    /// Operation produced a value (boxed to keep enum small)
14    Value(Box<LiteralValue>),
15    /// Operation was vetoed (valid result, no value)
16    Veto(Option<String>),
17}
18
19impl OperationResult {
20    pub fn vetoed(&self) -> bool {
21        matches!(self, OperationResult::Veto(_))
22    }
23
24    #[must_use]
25    pub fn value(&self) -> Option<&LiteralValue> {
26        match self {
27            OperationResult::Value(v) => Some(v.as_ref()),
28            OperationResult::Veto(_) => None,
29        }
30    }
31}
32
33/// The kind of computation performed
34#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
35#[serde(tag = "type", content = "computation", rename_all = "snake_case")]
36pub enum ComputationKind {
37    Arithmetic(ArithmeticComputation),
38    Comparison(ComparisonComputation),
39    Logical(LogicalComputation),
40    Mathematical(MathematicalComputation),
41}
42
43/// A record of a single operation during evaluation
44#[derive(Debug, Clone, Serialize)]
45pub struct OperationRecord {
46    #[serde(flatten)]
47    pub kind: OperationKind,
48}
49
50/// The kind of operation performed
51#[derive(Debug, Clone, Serialize)]
52#[serde(tag = "type", rename_all = "snake_case")]
53pub enum OperationKind {
54    FactUsed {
55        fact_ref: FactPath,
56        value: LiteralValue,
57    },
58    RuleUsed {
59        rule_path: RulePath,
60        result: OperationResult,
61    },
62    Computation {
63        kind: ComputationKind,
64        inputs: Vec<LiteralValue>,
65        result: LiteralValue,
66    },
67    RuleBranchEvaluated {
68        #[serde(skip_serializing_if = "Option::is_none")]
69        index: Option<usize>,
70        matched: bool,
71        #[serde(skip_serializing_if = "Option::is_none", default)]
72        result_value: Option<OperationResult>,
73    },
74}
75
76#[cfg(test)]
77mod computation_kind_serde_tests {
78    use super::ComputationKind;
79    use crate::parsing::ast::{
80        ArithmeticComputation, ComparisonComputation, MathematicalComputation,
81    };
82    use crate::planning::semantics::LogicalComputation;
83
84    #[test]
85    fn computation_kind_arithmetic_round_trip() {
86        let k = ComputationKind::Arithmetic(ArithmeticComputation::Add);
87        let json = serde_json::to_string(&k).expect("serialize");
88        assert!(json.contains("\"type\"") && json.contains("\"computation\""));
89        let back: ComputationKind = serde_json::from_str(&json).expect("deserialize");
90        assert_eq!(back, k);
91    }
92
93    #[test]
94    fn computation_kind_comparison_round_trip() {
95        let k = ComputationKind::Comparison(ComparisonComputation::GreaterThan);
96        let json = serde_json::to_string(&k).expect("serialize");
97        let back: ComputationKind = serde_json::from_str(&json).expect("deserialize");
98        assert_eq!(back, k);
99    }
100
101    #[test]
102    fn computation_kind_logical_round_trip() {
103        let k = ComputationKind::Logical(LogicalComputation::And);
104        let json = serde_json::to_string(&k).expect("serialize");
105        let back: ComputationKind = serde_json::from_str(&json).expect("deserialize");
106        assert_eq!(back, k);
107    }
108
109    #[test]
110    fn computation_kind_mathematical_round_trip() {
111        let k = ComputationKind::Mathematical(MathematicalComputation::Sqrt);
112        let json = serde_json::to_string(&k).expect("serialize");
113        let back: ComputationKind = serde_json::from_str(&json).expect("deserialize");
114        assert_eq!(back, k);
115    }
116}