rust_rule_engine/backward/
rule_executor.rs

1/// Rule execution for backward chaining
2///
3/// This module provides proper condition evaluation and action execution
4/// for backward chaining, replacing the "fake" stub implementations.
5
6use crate::engine::rule::{Condition, ConditionGroup, Rule};
7use crate::engine::condition_evaluator::ConditionEvaluator;
8use crate::types::{ActionType, Value};
9use crate::{Facts, KnowledgeBase};
10use crate::errors::{Result, RuleEngineError};
11
12/// Rule executor for backward chaining
13pub struct RuleExecutor {
14    knowledge_base: KnowledgeBase,
15    evaluator: ConditionEvaluator,
16}
17
18impl RuleExecutor {
19    /// Create a new rule executor
20    pub fn new(knowledge_base: KnowledgeBase) -> Self {
21        Self {
22            knowledge_base,
23            evaluator: ConditionEvaluator::with_builtin_functions(),
24        }
25    }
26
27    /// Check if rule conditions are satisfied and execute if they are
28    ///
29    /// Returns:
30    /// - Ok(true) if rule executed successfully
31    /// - Ok(false) if conditions not satisfied
32    /// - Err if execution failed
33    pub fn try_execute_rule(
34        &self,
35        rule: &Rule,
36        facts: &mut Facts,
37    ) -> Result<bool> {
38        // Check if all conditions are satisfied
39        if !self.evaluate_conditions(&rule.conditions, facts)? {
40            return Ok(false);
41        }
42
43        // Conditions satisfied - execute actions
44        self.execute_actions(rule, facts)?;
45
46        Ok(true)
47    }
48
49    /// Evaluate condition group
50    pub fn evaluate_conditions(
51        &self,
52        group: &ConditionGroup,
53        facts: &Facts,
54    ) -> Result<bool> {
55        // Delegate to shared evaluator
56        self.evaluator.evaluate_conditions(group, facts)
57    }
58
59    /// Evaluate a single condition
60    pub fn evaluate_condition(&self, condition: &Condition, facts: &Facts) -> Result<bool> {
61        // Delegate to shared evaluator
62        self.evaluator.evaluate_condition(condition, facts)
63    }
64
65    /// Execute rule actions
66    fn execute_actions(&self, rule: &Rule, facts: &mut Facts) -> Result<()> {
67        for action in &rule.actions {
68            self.execute_action(action, facts)?;
69        }
70
71        Ok(())
72    }
73
74    /// Execute a single action
75    fn execute_action(&self, action: &ActionType, facts: &mut Facts) -> Result<()> {
76        match action {
77            ActionType::Set { field, value } => {
78                // Evaluate value expression if needed
79                let evaluated_value = self.evaluate_value_expression(value, facts)?;
80                facts.set(field, evaluated_value);
81                Ok(())
82            }
83
84            ActionType::MethodCall { object, method, args } => {
85                // Execute method call
86                if let Some(obj_value) = facts.get(object) {
87                    let mut obj_value = obj_value.clone();
88                    // Evaluate arguments
89                    let mut arg_values = Vec::new();
90                    for arg in args {
91                        let val = self.evaluate_value_expression(arg, facts)?;
92                        arg_values.push(val);
93                    }
94
95                    // Call method
96                    let result = obj_value.call_method(method, arg_values)
97                        .map_err(|e| RuleEngineError::ExecutionError(e))?;
98
99                    // Update object
100                    facts.set(object, obj_value);
101
102                    // Store result if there's a return value
103                    if result != Value::Null {
104                        facts.set(&format!("{}._return", object), result);
105                    }
106
107                    Ok(())
108                } else {
109                    Err(RuleEngineError::ExecutionError(
110                        format!("Object not found: {}", object)
111                    ))
112                }
113            }
114
115            ActionType::Retract { object } => {
116                // Retract fact from working memory
117                // In backward chaining, we just remove the fact
118                facts.remove(object);
119                Ok(())
120            }
121
122            ActionType::Log { message } => {
123                // Just log for now
124                println!("[BC Action] {}", message);
125                Ok(())
126            }
127
128            ActionType::Custom { .. } => {
129                // Custom actions not supported in backward chaining yet
130                Ok(())
131            }
132
133            ActionType::ActivateAgendaGroup { .. } => {
134                // Agenda groups not supported in backward chaining
135                Ok(())
136            }
137
138            ActionType::ScheduleRule { .. } => {
139                // Rule scheduling not supported in backward chaining
140                Ok(())
141            }
142
143            ActionType::CompleteWorkflow { .. } => {
144                // Workflows not supported in backward chaining
145                Ok(())
146            }
147
148            ActionType::SetWorkflowData { .. } => {
149                // Workflow data not supported in backward chaining
150                Ok(())
151            }
152        }
153    }
154
155    /// Evaluate value expression
156    fn evaluate_value_expression(&self, value: &Value, facts: &Facts) -> Result<Value> {
157        match value {
158            Value::Expression(expr) => {
159                // Parse and evaluate expression
160                // For now, simple field reference or literal
161                if let Some(val) = facts.get(expr).or_else(|| facts.get_nested(expr)) {
162                    Ok(val)
163                } else {
164                    // Try to parse as literal using simple parsing
165                    if expr == "true" {
166                        Ok(Value::Boolean(true))
167                    } else if expr == "false" {
168                        Ok(Value::Boolean(false))
169                    } else if expr == "null" {
170                        Ok(Value::Null)
171                    } else if let Ok(n) = expr.parse::<f64>() {
172                        Ok(Value::Number(n))
173                    } else if let Ok(i) = expr.parse::<i64>() {
174                        Ok(Value::Integer(i))
175                    } else {
176                        Ok(value.clone())
177                    }
178                }
179            }
180            _ => Ok(value.clone()),
181        }
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188    use crate::types::Operator;
189
190    #[test]
191    fn test_evaluate_simple_condition() {
192        let kb = KnowledgeBase::new("test");
193        let executor = RuleExecutor::new(kb);
194
195        let mut facts = Facts::new();
196        facts.set("User.Age", Value::Number(25.0));
197
198        let condition = Condition::new(
199            "User.Age".to_string(),
200            Operator::GreaterThan,
201            Value::Number(18.0),
202        );
203
204        let result = executor.evaluate_condition(&condition, &facts).unwrap();
205        assert!(result);
206    }
207
208    #[test]
209    fn test_evaluate_function_call_len() {
210        let kb = KnowledgeBase::new("test");
211        let executor = RuleExecutor::new(kb);
212
213        let mut facts = Facts::new();
214        facts.set("User.Name", Value::String("John".to_string()));
215
216        let condition = Condition::with_function(
217            "len".to_string(),
218            vec!["User.Name".to_string()],
219            Operator::GreaterThan,
220            Value::Number(3.0),
221        );
222
223        let result = executor.evaluate_condition(&condition, &facts).unwrap();
224        assert!(result); // "John".len() = 4 > 3
225    }
226
227    #[test]
228    fn test_execute_set_action() {
229        let kb = KnowledgeBase::new("test");
230        let executor = RuleExecutor::new(kb);
231
232        let mut facts = Facts::new();
233
234        let action = ActionType::Set {
235            field: "User.IsVIP".to_string(),
236            value: Value::Boolean(true),
237        };
238
239        executor.execute_action(&action, &mut facts).unwrap();
240
241        assert_eq!(facts.get("User.IsVIP"), Some(Value::Boolean(true)));
242    }
243}