rust_rule_engine/backward/
rule_executor.rs1use 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
12pub struct RuleExecutor {
14 knowledge_base: KnowledgeBase,
15 evaluator: ConditionEvaluator,
16}
17
18impl RuleExecutor {
19 pub fn new(knowledge_base: KnowledgeBase) -> Self {
21 Self {
22 knowledge_base,
23 evaluator: ConditionEvaluator::with_builtin_functions(),
24 }
25 }
26
27 pub fn try_execute_rule(
34 &self,
35 rule: &Rule,
36 facts: &mut Facts,
37 ) -> Result<bool> {
38 if !self.evaluate_conditions(&rule.conditions, facts)? {
40 return Ok(false);
41 }
42
43 self.execute_actions(rule, facts)?;
45
46 Ok(true)
47 }
48
49 pub fn evaluate_conditions(
51 &self,
52 group: &ConditionGroup,
53 facts: &Facts,
54 ) -> Result<bool> {
55 self.evaluator.evaluate_conditions(group, facts)
57 }
58
59 pub fn evaluate_condition(&self, condition: &Condition, facts: &Facts) -> Result<bool> {
61 self.evaluator.evaluate_condition(condition, facts)
63 }
64
65 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 fn execute_action(&self, action: &ActionType, facts: &mut Facts) -> Result<()> {
76 match action {
77 ActionType::Set { field, value } => {
78 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 if let Some(obj_value) = facts.get(object) {
87 let mut obj_value = obj_value.clone();
88 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 let result = obj_value.call_method(method, arg_values)
97 .map_err(|e| RuleEngineError::ExecutionError(e))?;
98
99 facts.set(object, obj_value);
101
102 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 facts.remove(object);
119 Ok(())
120 }
121
122 ActionType::Log { message } => {
123 println!("[BC Action] {}", message);
125 Ok(())
126 }
127
128 ActionType::Custom { .. } => {
129 Ok(())
131 }
132
133 ActionType::ActivateAgendaGroup { .. } => {
134 Ok(())
136 }
137
138 ActionType::ScheduleRule { .. } => {
139 Ok(())
141 }
142
143 ActionType::CompleteWorkflow { .. } => {
144 Ok(())
146 }
147
148 ActionType::SetWorkflowData { .. } => {
149 Ok(())
151 }
152 }
153 }
154
155 fn evaluate_value_expression(&self, value: &Value, facts: &Facts) -> Result<Value> {
157 match value {
158 Value::Expression(expr) => {
159 if let Some(val) = facts.get(expr).or_else(|| facts.get_nested(expr)) {
162 Ok(val)
163 } else {
164 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); }
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}