1use crate::engine::{analytics::RuleAnalytics, facts::Facts, knowledge_base::KnowledgeBase};
2use crate::errors::{Result, RuleEngineError};
3use crate::types::{ActionType, Value};
4use std::collections::HashMap;
5use std::time::{Duration, Instant};
6
7pub type CustomFunction = Box<dyn Fn(&[Value], &Facts) -> Result<Value> + Send + Sync>;
9
10#[derive(Debug, Clone)]
12pub struct EngineConfig {
13 pub max_cycles: usize,
15 pub timeout: Option<Duration>,
17 pub enable_stats: bool,
19 pub debug_mode: bool,
21}
22
23impl Default for EngineConfig {
24 fn default() -> Self {
25 Self {
26 max_cycles: 100,
27 timeout: Some(Duration::from_secs(30)),
28 enable_stats: true,
29 debug_mode: false,
30 }
31 }
32}
33
34#[derive(Debug, Clone)]
36pub struct GruleExecutionResult {
37 pub cycle_count: usize,
39 pub rules_evaluated: usize,
41 pub rules_fired: usize,
43 pub execution_time: Duration,
45}
46
47pub struct RustRuleEngine {
49 knowledge_base: KnowledgeBase,
50 config: EngineConfig,
51 custom_functions: HashMap<String, CustomFunction>,
52 analytics: Option<RuleAnalytics>,
53}
54
55impl RustRuleEngine {
56 pub fn new(knowledge_base: KnowledgeBase) -> Self {
58 Self {
59 knowledge_base,
60 config: EngineConfig::default(),
61 custom_functions: HashMap::new(),
62 analytics: None,
63 }
64 }
65
66 pub fn with_config(knowledge_base: KnowledgeBase, config: EngineConfig) -> Self {
68 Self {
69 knowledge_base,
70 config,
71 custom_functions: HashMap::new(),
72 analytics: None,
73 }
74 }
75
76 pub fn register_function<F>(&mut self, name: &str, func: F)
78 where
79 F: Fn(&[Value], &Facts) -> Result<Value> + Send + Sync + 'static,
80 {
81 self.custom_functions
82 .insert(name.to_string(), Box::new(func));
83 }
84
85 pub fn enable_analytics(&mut self, analytics: RuleAnalytics) {
87 self.analytics = Some(analytics);
88 }
89
90 pub fn disable_analytics(&mut self) {
92 self.analytics = None;
93 }
94
95 pub fn analytics(&self) -> Option<&RuleAnalytics> {
97 self.analytics.as_ref()
98 }
99
100 pub fn has_function(&self, name: &str) -> bool {
102 self.custom_functions.contains_key(name)
103 }
104
105 pub fn knowledge_base(&self) -> &KnowledgeBase {
107 &self.knowledge_base
108 }
109
110 pub fn knowledge_base_mut(&mut self) -> &mut KnowledgeBase {
112 &mut self.knowledge_base
113 }
114
115 pub fn execute(&mut self, facts: &Facts) -> Result<GruleExecutionResult> {
117 let start_time = Instant::now();
118 let mut cycle_count = 0;
119 let mut rules_evaluated = 0;
120 let mut rules_fired = 0;
121
122 if self.config.debug_mode {
123 println!(
124 "🚀 Starting rule execution with {} rules",
125 self.knowledge_base.get_rules().len()
126 );
127 }
128
129 for cycle in 0..self.config.max_cycles {
130 cycle_count = cycle + 1;
131 let mut any_rule_fired = false;
132 let mut fired_rules_in_cycle = std::collections::HashSet::new();
133
134 if let Some(timeout) = self.config.timeout {
136 if start_time.elapsed() > timeout {
137 return Err(RuleEngineError::EvaluationError {
138 message: "Execution timeout exceeded".to_string(),
139 });
140 }
141 }
142
143 let mut rules = self.knowledge_base.get_rules().clone();
145 rules.sort_by(|a, b| b.salience.cmp(&a.salience));
146
147 for rule in &rules {
148 if !rule.enabled {
149 continue;
150 }
151
152 if rule.no_loop && fired_rules_in_cycle.contains(&rule.name) {
154 continue;
155 }
156
157 rules_evaluated += 1;
158 let rule_start = Instant::now();
159
160 if self.config.debug_mode {
161 println!("📝 Evaluating rule: {}", rule.name);
162 }
163
164 if self.evaluate_conditions(&rule.conditions, facts)? {
166 if self.config.debug_mode {
167 println!(
168 "🔥 Rule '{}' fired (salience: {})",
169 rule.name, rule.salience
170 );
171 }
172
173 for action in &rule.actions {
175 self.execute_action(action, facts)?;
176 }
177
178 let rule_duration = rule_start.elapsed();
179
180 if let Some(analytics) = &mut self.analytics {
182 analytics.record_execution(&rule.name, rule_duration, true, true, None, 0);
183 }
184
185 rules_fired += 1;
186 any_rule_fired = true;
187
188 fired_rules_in_cycle.insert(rule.name.clone());
190 } else {
191 let rule_duration = rule_start.elapsed();
192
193 if let Some(analytics) = &mut self.analytics {
195 analytics.record_execution(
196 &rule.name,
197 rule_duration,
198 false,
199 false,
200 None,
201 0,
202 );
203 }
204 }
205 }
206
207 if !any_rule_fired {
209 break;
210 }
211 }
212
213 let execution_time = start_time.elapsed();
214
215 Ok(GruleExecutionResult {
216 cycle_count,
217 rules_evaluated,
218 rules_fired,
219 execution_time,
220 })
221 }
222
223 fn evaluate_conditions(
225 &self,
226 conditions: &crate::engine::rule::ConditionGroup,
227 facts: &Facts,
228 ) -> Result<bool> {
229 use crate::engine::rule::ConditionGroup;
230
231 match conditions {
232 ConditionGroup::Single(condition) => self.evaluate_single_condition(condition, facts),
233 ConditionGroup::Compound {
234 left,
235 operator,
236 right,
237 } => {
238 let left_result = self.evaluate_conditions(left, facts)?;
239 let right_result = self.evaluate_conditions(right, facts)?;
240
241 match operator {
242 crate::types::LogicalOperator::And => Ok(left_result && right_result),
243 crate::types::LogicalOperator::Or => Ok(left_result || right_result),
244 crate::types::LogicalOperator::Not => Err(RuleEngineError::EvaluationError {
245 message: "NOT operator should not appear in compound conditions"
246 .to_string(),
247 }),
248 }
249 }
250 ConditionGroup::Not(condition) => {
251 let result = self.evaluate_conditions(condition, facts)?;
252 Ok(!result)
253 }
254 }
255 }
256
257 fn evaluate_single_condition(
259 &self,
260 condition: &crate::engine::rule::Condition,
261 facts: &Facts,
262 ) -> Result<bool> {
263 let field_value = facts.get_nested(&condition.field);
265
266 if field_value.is_none() {
267 return Ok(false); }
269
270 let field_value = field_value.unwrap();
271
272 Ok(condition.operator.evaluate(&field_value, &condition.value))
274 }
275
276 fn execute_action(&self, action: &ActionType, facts: &Facts) -> Result<()> {
278 match action {
279 ActionType::Set { field, value } => {
280 facts.set_nested(field, value.clone())?;
281 if self.config.debug_mode {
282 println!(" ✅ Set {field} = {value:?}");
283 }
284 }
285 ActionType::Log { message } => {
286 println!("📋 LOG: {}", message);
287 }
288 ActionType::Call { function, args } => {
289 let result = self.execute_function_call(function, args, facts)?;
290 if self.config.debug_mode {
291 println!(" 📞 Called {function}({args:?}) -> {result}");
292 }
293 }
294 ActionType::MethodCall {
295 object,
296 method,
297 args,
298 } => {
299 let result = self.execute_method_call(object, method, args, facts)?;
300 if self.config.debug_mode {
301 println!(" 🔧 Called {object}.{method}({args:?}) -> {result}");
302 }
303 }
304 ActionType::Update { object } => {
305 if self.config.debug_mode {
306 println!(" 🔄 Updated {object}");
307 }
308 }
311 ActionType::Custom {
312 action_type,
313 params,
314 } => {
315 if self.config.debug_mode {
316 println!(" 🎯 Custom action: {action_type} with params: {params:?}");
317 }
318 }
319 }
320 Ok(())
321 }
322
323 fn execute_function_call(
325 &self,
326 function: &str,
327 args: &[Value],
328 facts: &Facts,
329 ) -> Result<String> {
330 let function_lower = function.to_lowercase();
331
332 match function_lower.as_str() {
334 "log" | "print" | "println" => self.handle_log_function(args),
335 "update" | "refresh" => self.handle_update_function(args),
336 "now" | "timestamp" => self.handle_timestamp_function(),
337 "random" => self.handle_random_function(args),
338 "format" | "sprintf" => self.handle_format_function(args),
339 "length" | "size" | "count" => self.handle_length_function(args),
340 "sum" | "add" => self.handle_sum_function(args),
341 "max" | "maximum" => self.handle_max_function(args),
342 "min" | "minimum" => self.handle_min_function(args),
343 "avg" | "average" => self.handle_average_function(args),
344 "round" => self.handle_round_function(args),
345 "floor" => self.handle_floor_function(args),
346 "ceil" | "ceiling" => self.handle_ceil_function(args),
347 "abs" | "absolute" => self.handle_abs_function(args),
348 "contains" | "includes" => self.handle_contains_function(args),
349 "startswith" | "begins_with" => self.handle_starts_with_function(args),
350 "endswith" | "ends_with" => self.handle_ends_with_function(args),
351 "lowercase" | "tolower" => self.handle_lowercase_function(args),
352 "uppercase" | "toupper" => self.handle_uppercase_function(args),
353 "trim" | "strip" => self.handle_trim_function(args),
354 "split" => self.handle_split_function(args),
355 "join" => self.handle_join_function(args),
356 _ => {
357 self.handle_custom_function(function, args, facts)
359 }
360 }
361 }
362
363 fn handle_log_function(&self, args: &[Value]) -> Result<String> {
365 let message = if args.is_empty() {
366 "".to_string()
367 } else if args.len() == 1 {
368 args[0].to_string()
369 } else {
370 args.iter()
371 .map(|v| v.to_string())
372 .collect::<Vec<_>>()
373 .join(" ")
374 };
375
376 println!("📋 {}", message);
377 Ok(message)
378 }
379
380 fn handle_update_function(&self, args: &[Value]) -> Result<String> {
382 if let Some(arg) = args.first() {
383 Ok(format!("Updated: {}", arg.to_string()))
384 } else {
385 Ok("Updated".to_string())
386 }
387 }
388
389 fn handle_timestamp_function(&self) -> Result<String> {
391 use std::time::{SystemTime, UNIX_EPOCH};
392 let timestamp = SystemTime::now()
393 .duration_since(UNIX_EPOCH)
394 .map_err(|e| RuleEngineError::EvaluationError {
395 message: format!("Failed to get timestamp: {}", e),
396 })?
397 .as_secs();
398 Ok(timestamp.to_string())
399 }
400
401 fn handle_random_function(&self, args: &[Value]) -> Result<String> {
403 use std::collections::hash_map::DefaultHasher;
404 use std::hash::{Hash, Hasher};
405
406 let mut hasher = DefaultHasher::new();
408 std::time::SystemTime::now().hash(&mut hasher);
409 let random_value = hasher.finish();
410
411 if args.is_empty() {
412 Ok((random_value % 100).to_string()) } else if let Some(Value::Number(max)) = args.first() {
414 let max_val = *max as u64;
415 Ok((random_value % max_val).to_string())
416 } else {
417 Ok(random_value.to_string())
418 }
419 }
420
421 fn handle_format_function(&self, args: &[Value]) -> Result<String> {
423 if args.is_empty() {
424 return Ok("".to_string());
425 }
426
427 let template = args[0].to_string();
428 let values: Vec<String> = args[1..].iter().map(|v| v.to_string()).collect();
429
430 let mut result = template;
432 for (i, value) in values.iter().enumerate() {
433 result = result.replace(&format!("{{{}}}", i), value);
434 }
435
436 Ok(result)
437 }
438
439 fn handle_length_function(&self, args: &[Value]) -> Result<String> {
441 if let Some(arg) = args.first() {
442 match arg {
443 Value::String(s) => Ok(s.len().to_string()),
444 Value::Array(arr) => Ok(arr.len().to_string()),
445 Value::Object(obj) => Ok(obj.len().to_string()),
446 _ => Ok("1".to_string()), }
448 } else {
449 Ok("0".to_string())
450 }
451 }
452
453 fn handle_sum_function(&self, args: &[Value]) -> Result<String> {
455 let sum = args.iter().fold(0.0, |acc, val| match val {
456 Value::Number(n) => acc + n,
457 Value::Integer(i) => acc + (*i as f64),
458 _ => acc,
459 });
460 Ok(sum.to_string())
461 }
462
463 fn handle_max_function(&self, args: &[Value]) -> Result<String> {
465 let max = args.iter().fold(f64::NEG_INFINITY, |acc, val| match val {
466 Value::Number(n) => acc.max(*n),
467 Value::Integer(i) => acc.max(*i as f64),
468 _ => acc,
469 });
470 Ok(max.to_string())
471 }
472
473 fn handle_min_function(&self, args: &[Value]) -> Result<String> {
475 let min = args.iter().fold(f64::INFINITY, |acc, val| match val {
476 Value::Number(n) => acc.min(*n),
477 Value::Integer(i) => acc.min(*i as f64),
478 _ => acc,
479 });
480 Ok(min.to_string())
481 }
482
483 fn handle_average_function(&self, args: &[Value]) -> Result<String> {
485 if args.is_empty() {
486 return Ok("0".to_string());
487 }
488
489 let (sum, count) = args.iter().fold((0.0, 0), |(sum, count), val| match val {
490 Value::Number(n) => (sum + n, count + 1),
491 Value::Integer(i) => (sum + (*i as f64), count + 1),
492 _ => (sum, count),
493 });
494
495 if count > 0 {
496 Ok((sum / count as f64).to_string())
497 } else {
498 Ok("0".to_string())
499 }
500 }
501
502 fn handle_round_function(&self, args: &[Value]) -> Result<String> {
504 if let Some(Value::Number(n)) = args.first() {
505 Ok(n.round().to_string())
506 } else if let Some(Value::Integer(i)) = args.first() {
507 Ok(i.to_string())
508 } else {
509 Err(RuleEngineError::EvaluationError {
510 message: "round() requires a numeric argument".to_string(),
511 })
512 }
513 }
514
515 fn handle_floor_function(&self, args: &[Value]) -> Result<String> {
516 if let Some(Value::Number(n)) = args.first() {
517 Ok(n.floor().to_string())
518 } else if let Some(Value::Integer(i)) = args.first() {
519 Ok(i.to_string())
520 } else {
521 Err(RuleEngineError::EvaluationError {
522 message: "floor() requires a numeric argument".to_string(),
523 })
524 }
525 }
526
527 fn handle_ceil_function(&self, args: &[Value]) -> Result<String> {
528 if let Some(Value::Number(n)) = args.first() {
529 Ok(n.ceil().to_string())
530 } else if let Some(Value::Integer(i)) = args.first() {
531 Ok(i.to_string())
532 } else {
533 Err(RuleEngineError::EvaluationError {
534 message: "ceil() requires a numeric argument".to_string(),
535 })
536 }
537 }
538
539 fn handle_abs_function(&self, args: &[Value]) -> Result<String> {
540 if let Some(Value::Number(n)) = args.first() {
541 Ok(n.abs().to_string())
542 } else if let Some(Value::Integer(i)) = args.first() {
543 Ok(i.abs().to_string())
544 } else {
545 Err(RuleEngineError::EvaluationError {
546 message: "abs() requires a numeric argument".to_string(),
547 })
548 }
549 }
550
551 fn handle_contains_function(&self, args: &[Value]) -> Result<String> {
553 if args.len() >= 2 {
554 let haystack = args[0].to_string();
555 let needle = args[1].to_string();
556 Ok(haystack.contains(&needle).to_string())
557 } else {
558 Err(RuleEngineError::EvaluationError {
559 message: "contains() requires 2 arguments".to_string(),
560 })
561 }
562 }
563
564 fn handle_starts_with_function(&self, args: &[Value]) -> Result<String> {
565 if args.len() >= 2 {
566 let text = args[0].to_string();
567 let prefix = args[1].to_string();
568 Ok(text.starts_with(&prefix).to_string())
569 } else {
570 Err(RuleEngineError::EvaluationError {
571 message: "startswith() requires 2 arguments".to_string(),
572 })
573 }
574 }
575
576 fn handle_ends_with_function(&self, args: &[Value]) -> Result<String> {
577 if args.len() >= 2 {
578 let text = args[0].to_string();
579 let suffix = args[1].to_string();
580 Ok(text.ends_with(&suffix).to_string())
581 } else {
582 Err(RuleEngineError::EvaluationError {
583 message: "endswith() requires 2 arguments".to_string(),
584 })
585 }
586 }
587
588 fn handle_lowercase_function(&self, args: &[Value]) -> Result<String> {
589 if let Some(arg) = args.first() {
590 Ok(arg.to_string().to_lowercase())
591 } else {
592 Err(RuleEngineError::EvaluationError {
593 message: "lowercase() requires 1 argument".to_string(),
594 })
595 }
596 }
597
598 fn handle_uppercase_function(&self, args: &[Value]) -> Result<String> {
599 if let Some(arg) = args.first() {
600 Ok(arg.to_string().to_uppercase())
601 } else {
602 Err(RuleEngineError::EvaluationError {
603 message: "uppercase() requires 1 argument".to_string(),
604 })
605 }
606 }
607
608 fn handle_trim_function(&self, args: &[Value]) -> Result<String> {
609 if let Some(arg) = args.first() {
610 Ok(arg.to_string().trim().to_string())
611 } else {
612 Err(RuleEngineError::EvaluationError {
613 message: "trim() requires 1 argument".to_string(),
614 })
615 }
616 }
617
618 fn handle_split_function(&self, args: &[Value]) -> Result<String> {
619 if args.len() >= 2 {
620 let text = args[0].to_string();
621 let delimiter = args[1].to_string();
622 let parts: Vec<String> = text.split(&delimiter).map(|s| s.to_string()).collect();
623 Ok(format!("{:?}", parts)) } else {
625 Err(RuleEngineError::EvaluationError {
626 message: "split() requires 2 arguments".to_string(),
627 })
628 }
629 }
630
631 fn handle_join_function(&self, args: &[Value]) -> Result<String> {
632 if args.len() >= 2 {
633 let delimiter = args[0].to_string();
634 let parts: Vec<String> = args[1..].iter().map(|v| v.to_string()).collect();
635 Ok(parts.join(&delimiter))
636 } else {
637 Err(RuleEngineError::EvaluationError {
638 message: "join() requires at least 2 arguments".to_string(),
639 })
640 }
641 }
642
643 fn handle_custom_function(
645 &self,
646 function: &str,
647 args: &[Value],
648 facts: &Facts,
649 ) -> Result<String> {
650 if let Some(custom_func) = self.custom_functions.get(function) {
652 if self.config.debug_mode {
653 println!("🎯 Calling registered function: {}({:?})", function, args);
654 }
655
656 match custom_func(args, facts) {
657 Ok(result) => Ok(result.to_string()),
658 Err(e) => Err(e),
659 }
660 } else {
661 if self.config.debug_mode {
663 println!("⚠️ Custom function '{}' not registered", function);
664 }
665
666 Err(RuleEngineError::EvaluationError {
667 message: format!("Function '{}' is not registered. Use engine.register_function() to add custom functions.", function),
668 })
669 }
670 }
671
672 fn execute_method_call(
674 &self,
675 object_name: &str,
676 method: &str,
677 args: &[Value],
678 facts: &Facts,
679 ) -> Result<String> {
680 let Some(object_value) = facts.get(object_name) else {
682 return Err(RuleEngineError::EvaluationError {
683 message: format!("Object '{}' not found in facts", object_name),
684 });
685 };
686
687 let method_lower = method.to_lowercase();
688
689 if method_lower.starts_with("set") && args.len() == 1 {
691 return self.handle_setter_method(object_name, method, &args[0], object_value, facts);
692 }
693
694 if method_lower.starts_with("get") && args.is_empty() {
696 return self.handle_getter_method(object_name, method, &object_value);
697 }
698
699 match method_lower.as_str() {
701 "tostring" => Ok(object_value.to_string()),
702 "update" => {
703 facts.add_value(object_name, object_value)?;
704 Ok(format!("Updated {}", object_name))
705 }
706 "reset" => self.handle_reset_method(object_name, object_value, facts),
707 _ => self.handle_property_access_or_fallback(
708 object_name,
709 method,
710 args.len(),
711 &object_value,
712 ),
713 }
714 }
715
716 fn handle_setter_method(
718 &self,
719 object_name: &str,
720 method: &str,
721 new_value: &Value,
722 mut object_value: Value,
723 facts: &Facts,
724 ) -> Result<String> {
725 let property_name = Self::extract_property_name_from_setter(method);
726
727 match object_value {
728 Value::Object(ref mut obj) => {
729 obj.insert(property_name.clone(), new_value.clone());
730 facts.add_value(object_name, object_value)?;
731 Ok(format!(
732 "Set {} to {}",
733 property_name,
734 new_value.to_string()
735 ))
736 }
737 _ => Err(RuleEngineError::EvaluationError {
738 message: format!("Cannot call setter on non-object type: {}", object_name),
739 }),
740 }
741 }
742
743 fn handle_getter_method(
745 &self,
746 object_name: &str,
747 method: &str,
748 object_value: &Value,
749 ) -> Result<String> {
750 let property_name = Self::extract_property_name_from_getter(method);
751
752 match object_value {
753 Value::Object(obj) => {
754 if let Some(value) = obj.get(&property_name) {
755 Ok(value.to_string())
756 } else {
757 Err(RuleEngineError::EvaluationError {
758 message: format!(
759 "Property '{}' not found on object '{}'",
760 property_name, object_name
761 ),
762 })
763 }
764 }
765 _ => Err(RuleEngineError::EvaluationError {
766 message: format!("Cannot call getter on non-object type: {}", object_name),
767 }),
768 }
769 }
770
771 fn handle_reset_method(
773 &self,
774 object_name: &str,
775 mut object_value: Value,
776 facts: &Facts,
777 ) -> Result<String> {
778 match object_value {
779 Value::Object(ref mut obj) => {
780 obj.clear();
781 facts.add_value(object_name, object_value)?;
782 Ok(format!("Reset {}", object_name))
783 }
784 _ => Err(RuleEngineError::EvaluationError {
785 message: format!("Cannot reset non-object type: {}", object_name),
786 }),
787 }
788 }
789
790 fn handle_property_access_or_fallback(
792 &self,
793 object_name: &str,
794 method: &str,
795 arg_count: usize,
796 object_value: &Value,
797 ) -> Result<String> {
798 if let Value::Object(obj) = object_value {
799 if let Some(value) = obj.get(method) {
801 return Ok(value.to_string());
802 }
803
804 let capitalized_method = Self::capitalize_first_letter(method);
806 if let Some(value) = obj.get(&capitalized_method) {
807 return Ok(value.to_string());
808 }
809 }
810
811 Ok(format!(
813 "Called {}.{} with {} args",
814 object_name, method, arg_count
815 ))
816 }
817
818 fn extract_property_name_from_setter(method: &str) -> String {
820 let property_name = &method[3..]; Self::capitalize_first_letter(property_name)
822 }
823
824 fn extract_property_name_from_getter(method: &str) -> String {
826 let property_name = &method[3..]; Self::capitalize_first_letter(property_name)
828 }
829
830 fn capitalize_first_letter(s: &str) -> String {
832 if s.is_empty() {
833 return String::new();
834 }
835 let mut chars = s.chars();
836 match chars.next() {
837 None => String::new(),
838 Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
839 }
840 }
841}