lemma/evaluator/
rules.rs

1//! Rule evaluation
2//!
3//! Handles evaluation of rules including default expressions and unless clauses.
4
5use super::context::EvaluationContext;
6use super::expression::evaluate_expression;
7use crate::{LemmaError, LemmaRule, OperationResult};
8
9/// Evaluate a rule to produce its final result
10///
11/// Unless clauses are evaluated in reverse order (last matching wins).
12/// If no unless clause matches, evaluate the default expression.
13pub fn evaluate_rule(
14    rule: &LemmaRule,
15    context: &mut EvaluationContext,
16) -> Result<OperationResult, LemmaError> {
17    use crate::OperationRecord;
18
19    // Evaluate unless clauses in reverse order (last matching wins)
20    for (index, unless_clause) in rule.unless_clauses.iter().enumerate().rev() {
21        let condition_result = evaluate_expression(&unless_clause.condition, context)?;
22
23        // If condition is vetoed, the veto applies to this rule
24        if let OperationResult::Veto(msg) = condition_result {
25            return Ok(OperationResult::Veto(msg));
26        }
27
28        let condition_value = condition_result.value().unwrap();
29        let matched = match condition_value {
30            crate::LiteralValue::Boolean(b) => *b,
31            _ => {
32                return Err(LemmaError::Engine(
33                    "Unless condition must evaluate to boolean".to_string(),
34                ));
35            }
36        };
37
38        if matched {
39            let result = evaluate_expression(&unless_clause.result, context)?;
40
41            // If result is vetoed, the veto applies to this rule
42            if let OperationResult::Veto(msg) = result {
43                return Ok(OperationResult::Veto(msg));
44            }
45
46            let result_value = result.value().unwrap().clone();
47            context
48                .operations
49                .push(OperationRecord::UnlessClauseEvaluated {
50                    index,
51                    matched: true,
52                    result_if_matched: Some(result_value.clone()),
53                });
54            context.operations.push(OperationRecord::FinalResult {
55                value: result_value.clone(),
56            });
57            return Ok(OperationResult::Value(result_value));
58        } else {
59            context
60                .operations
61                .push(OperationRecord::UnlessClauseEvaluated {
62                    index,
63                    matched: false,
64                    result_if_matched: None,
65                });
66        }
67    }
68
69    // No unless clause matched - evaluate default expression
70    let default_result = evaluate_expression(&rule.expression, context)?;
71
72    // If default is vetoed, the veto applies to this rule
73    if let OperationResult::Veto(msg) = default_result {
74        return Ok(OperationResult::Veto(msg));
75    }
76
77    let default_value = default_result.value().unwrap().clone();
78    context.operations.push(OperationRecord::DefaultValue {
79        value: default_value.clone(),
80    });
81    context.operations.push(OperationRecord::FinalResult {
82        value: default_value.clone(),
83    });
84    Ok(OperationResult::Value(default_value))
85}