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)) {
161 return Ok(val);
162 }
163
164 if let Some(result) = self.try_evaluate_arithmetic(expr, facts) {
166 return Ok(result);
167 }
168
169 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 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 fn try_evaluate_arithmetic(&self, expr: &str, facts: &Facts) -> Option<Value> {
204 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 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 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 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 fn get_numeric_value(&self, s: &str, facts: &Facts) -> Option<f64> {
256 if let Ok(n) = s.parse::<f64>() {
258 return Some(n);
259 }
260
261 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); }
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}