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                // Try simple field lookup first
160                if let Some(val) = facts.get(expr).or_else(|| facts.get_nested(expr)) {
161                    return Ok(val);
162                }
163
164                // Try arithmetic expression evaluation
165                if let Some(result) = self.try_evaluate_arithmetic(expr, facts) {
166                    return Ok(result);
167                }
168
169                // Try to parse as literal
170                if expr == "true" {
171                    Ok(Value::Boolean(true))
172                } else if expr == "false" {
173                    Ok(Value::Boolean(false))
174                } else if expr == "null" {
175                    Ok(Value::Null)
176                } else if let Ok(n) = expr.parse::<f64>() {
177                    Ok(Value::Number(n))
178                } else if let Ok(i) = expr.parse::<i64>() {
179                    Ok(Value::Integer(i))
180                } else {
181                    // Try to parse as literal using simple parsing
182                    if expr == "true" {
183                        Ok(Value::Boolean(true))
184                    } else if expr == "false" {
185                        Ok(Value::Boolean(false))
186                    } else if expr == "null" {
187                        Ok(Value::Null)
188                    } else if let Ok(n) = expr.parse::<f64>() {
189                        Ok(Value::Number(n))
190                    } else if let Ok(i) = expr.parse::<i64>() {
191                        Ok(Value::Integer(i))
192                    } else {
193                        Ok(value.clone())
194                    }
195                }
196            }
197            _ => Ok(value.clone()),
198        }
199    }
200
201    /// Try to evaluate simple arithmetic expressions
202    /// Supports: +, -, *, /
203    fn try_evaluate_arithmetic(&self, expr: &str, facts: &Facts) -> Option<Value> {
204        // Check for division
205        if let Some(div_pos) = expr.find(" / ") {
206            let left = expr[..div_pos].trim();
207            let right = expr[div_pos + 3..].trim();
208
209            let left_val = self.get_numeric_value(left, facts)?;
210            let right_val = self.get_numeric_value(right, facts)?;
211
212            if right_val != 0.0 {
213                return Some(Value::Number(left_val / right_val));
214            }
215            return None;
216        }
217
218        // Check for multiplication
219        if let Some(mul_pos) = expr.find(" * ") {
220            let left = expr[..mul_pos].trim();
221            let right = expr[mul_pos + 3..].trim();
222
223            let left_val = self.get_numeric_value(left, facts)?;
224            let right_val = self.get_numeric_value(right, facts)?;
225
226            return Some(Value::Number(left_val * right_val));
227        }
228
229        // Check for addition
230        if let Some(add_pos) = expr.find(" + ") {
231            let left = expr[..add_pos].trim();
232            let right = expr[add_pos + 3..].trim();
233
234            let left_val = self.get_numeric_value(left, facts)?;
235            let right_val = self.get_numeric_value(right, facts)?;
236
237            return Some(Value::Number(left_val + right_val));
238        }
239
240        // Check for subtraction
241        if let Some(sub_pos) = expr.find(" - ") {
242            let left = expr[..sub_pos].trim();
243            let right = expr[sub_pos + 3..].trim();
244
245            let left_val = self.get_numeric_value(left, facts)?;
246            let right_val = self.get_numeric_value(right, facts)?;
247
248            return Some(Value::Number(left_val - right_val));
249        }
250
251        None
252    }
253
254    /// Get numeric value from field name or literal
255    fn get_numeric_value(&self, s: &str, facts: &Facts) -> Option<f64> {
256        // Try parsing as number first
257        if let Ok(n) = s.parse::<f64>() {
258            return Some(n);
259        }
260
261        // Try getting from facts
262        if let Some(val) = facts.get(s).or_else(|| facts.get_nested(s)) {
263            match val {
264                Value::Number(n) => Some(n),
265                Value::Integer(i) => Some(i as f64),
266                _ => None,
267            }
268        } else {
269            None
270        }
271    }
272}
273
274#[cfg(test)]
275mod tests {
276    use super::*;
277    use crate::types::Operator;
278
279    #[test]
280    fn test_evaluate_simple_condition() {
281        let kb = KnowledgeBase::new("test");
282        let executor = RuleExecutor::new(kb);
283
284        let mut facts = Facts::new();
285        facts.set("User.Age", Value::Number(25.0));
286
287        let condition = Condition::new(
288            "User.Age".to_string(),
289            Operator::GreaterThan,
290            Value::Number(18.0),
291        );
292
293        let result = executor.evaluate_condition(&condition, &facts).unwrap();
294        assert!(result);
295    }
296
297    #[test]
298    fn test_evaluate_function_call_len() {
299        let kb = KnowledgeBase::new("test");
300        let executor = RuleExecutor::new(kb);
301
302        let mut facts = Facts::new();
303        facts.set("User.Name", Value::String("John".to_string()));
304
305        let condition = Condition::with_function(
306            "len".to_string(),
307            vec!["User.Name".to_string()],
308            Operator::GreaterThan,
309            Value::Number(3.0),
310        );
311
312        let result = executor.evaluate_condition(&condition, &facts).unwrap();
313        assert!(result); // "John".len() = 4 > 3
314    }
315
316    #[test]
317    fn test_execute_set_action() {
318        let kb = KnowledgeBase::new("test");
319        let executor = RuleExecutor::new(kb);
320
321        let mut facts = Facts::new();
322
323        let action = ActionType::Set {
324            field: "User.IsVIP".to_string(),
325            value: Value::Boolean(true),
326        };
327
328        executor.execute_action(&action, &mut facts).unwrap();
329
330        assert_eq!(facts.get("User.IsVIP"), Some(Value::Boolean(true)));
331    }
332}