1use crate::engine::{
2 agenda::{ActivationGroupManager, AgendaManager},
3 analytics::RuleAnalytics,
4 facts::Facts,
5 knowledge_base::KnowledgeBase,
6};
7use crate::errors::{Result, RuleEngineError};
8use crate::types::{ActionType, Value};
9use chrono::{DateTime, Utc};
10use std::collections::HashMap;
11use std::time::{Duration, Instant};
12
13pub type CustomFunction = Box<dyn Fn(&[Value], &Facts) -> Result<Value> + Send + Sync>;
15
16pub type ActionHandler = Box<dyn Fn(&HashMap<String, Value>, &Facts) -> Result<()> + Send + Sync>;
18
19#[derive(Debug, Clone)]
21pub struct EngineConfig {
22 pub max_cycles: usize,
24 pub timeout: Option<Duration>,
26 pub enable_stats: bool,
28 pub debug_mode: bool,
30}
31
32impl Default for EngineConfig {
33 fn default() -> Self {
34 Self {
35 max_cycles: 100,
36 timeout: Some(Duration::from_secs(30)),
37 enable_stats: true,
38 debug_mode: false,
39 }
40 }
41}
42
43#[derive(Debug, Clone)]
45pub struct GruleExecutionResult {
46 pub cycle_count: usize,
48 pub rules_evaluated: usize,
50 pub rules_fired: usize,
52 pub execution_time: Duration,
54}
55
56pub struct RustRuleEngine {
58 knowledge_base: KnowledgeBase,
59 config: EngineConfig,
60 custom_functions: HashMap<String, CustomFunction>,
61 action_handlers: HashMap<String, ActionHandler>,
62 analytics: Option<RuleAnalytics>,
63 agenda_manager: AgendaManager,
64 activation_group_manager: ActivationGroupManager,
65 fired_rules_global: std::collections::HashSet<String>,
67}
68
69impl RustRuleEngine {
70 pub fn new(knowledge_base: KnowledgeBase) -> Self {
72 Self {
73 knowledge_base,
74 config: EngineConfig::default(),
75 custom_functions: HashMap::new(),
76 action_handlers: HashMap::new(),
77 analytics: None,
78 agenda_manager: AgendaManager::new(),
79 activation_group_manager: ActivationGroupManager::new(),
80 fired_rules_global: std::collections::HashSet::new(),
81 }
82 }
83
84 pub fn with_config(knowledge_base: KnowledgeBase, config: EngineConfig) -> Self {
86 Self {
87 knowledge_base,
88 config,
89 custom_functions: HashMap::new(),
90 action_handlers: HashMap::new(),
91 analytics: None,
92 agenda_manager: AgendaManager::new(),
93 activation_group_manager: ActivationGroupManager::new(),
94 fired_rules_global: std::collections::HashSet::new(),
95 }
96 }
97
98 pub fn register_function<F>(&mut self, name: &str, func: F)
100 where
101 F: Fn(&[Value], &Facts) -> Result<Value> + Send + Sync + 'static,
102 {
103 self.custom_functions
104 .insert(name.to_string(), Box::new(func));
105 }
106
107 pub fn register_action_handler<F>(&mut self, action_type: &str, handler: F)
109 where
110 F: Fn(&HashMap<String, Value>, &Facts) -> Result<()> + Send + Sync + 'static,
111 {
112 self.action_handlers
113 .insert(action_type.to_string(), Box::new(handler));
114 }
115
116 pub fn enable_analytics(&mut self, analytics: RuleAnalytics) {
118 self.analytics = Some(analytics);
119 }
120
121 pub fn reset_no_loop_tracking(&mut self) {
123 self.fired_rules_global.clear();
124 }
125
126 pub fn disable_analytics(&mut self) {
128 self.analytics = None;
129 }
130
131 pub fn analytics(&self) -> Option<&RuleAnalytics> {
133 self.analytics.as_ref()
134 }
135
136 pub fn has_function(&self, name: &str) -> bool {
138 self.custom_functions.contains_key(name)
139 }
140
141 pub fn has_action_handler(&self, action_type: &str) -> bool {
143 self.action_handlers.contains_key(action_type)
144 }
145
146 pub fn knowledge_base(&self) -> &KnowledgeBase {
148 &self.knowledge_base
149 }
150
151 pub fn knowledge_base_mut(&mut self) -> &mut KnowledgeBase {
153 &mut self.knowledge_base
154 }
155
156 pub fn set_agenda_focus(&mut self, group: &str) {
158 self.agenda_manager.set_focus(group);
159 }
160
161 pub fn get_active_agenda_group(&self) -> &str {
163 self.agenda_manager.get_active_group()
164 }
165
166 pub fn pop_agenda_focus(&mut self) -> Option<String> {
168 self.agenda_manager.pop_focus()
169 }
170
171 pub fn clear_agenda_focus(&mut self) {
173 self.agenda_manager.clear_focus();
174 }
175
176 pub fn get_agenda_groups(&self) -> Vec<String> {
178 self.agenda_manager
179 .get_agenda_groups(&self.knowledge_base.get_rules())
180 }
181
182 pub fn get_activation_groups(&self) -> Vec<String> {
184 self.activation_group_manager
185 .get_activation_groups(&self.knowledge_base.get_rules())
186 }
187
188 pub fn execute(&mut self, facts: &Facts) -> Result<GruleExecutionResult> {
190 self.execute_at_time(facts, Utc::now())
191 }
192
193 pub fn execute_at_time(
195 &mut self,
196 facts: &Facts,
197 timestamp: DateTime<Utc>,
198 ) -> Result<GruleExecutionResult> {
199 let start_time = Instant::now();
200 let mut cycle_count = 0;
201 let mut rules_evaluated = 0;
202 let mut rules_fired = 0;
203
204 if self.config.debug_mode {
205 println!(
206 "🚀 Starting rule execution with {} rules (agenda group: {})",
207 self.knowledge_base.get_rules().len(),
208 self.agenda_manager.get_active_group()
209 );
210 }
211
212 for cycle in 0..self.config.max_cycles {
213 cycle_count = cycle + 1;
214 let mut any_rule_fired = false;
215 let mut fired_rules_in_cycle = std::collections::HashSet::new();
216
217 self.activation_group_manager.reset_cycle();
219
220 if let Some(timeout) = self.config.timeout {
222 if start_time.elapsed() > timeout {
223 return Err(RuleEngineError::EvaluationError {
224 message: "Execution timeout exceeded".to_string(),
225 });
226 }
227 }
228
229 let mut rules = self.knowledge_base.get_rules().clone();
231 rules.sort_by(|a, b| b.salience.cmp(&a.salience));
232
233 let rules: Vec<_> = rules
235 .iter()
236 .filter(|rule| self.agenda_manager.should_evaluate_rule(rule))
237 .collect();
238
239 for rule in &rules {
240 if !rule.enabled {
241 continue;
242 }
243
244 if !rule.is_active_at(timestamp) {
246 continue;
247 }
248
249 if !self.agenda_manager.can_fire_rule(rule) {
251 continue;
252 }
253
254 if !self.activation_group_manager.can_fire(rule) {
256 continue;
257 }
258
259 if rule.no_loop && self.fired_rules_global.contains(&rule.name) {
261 continue;
262 }
263
264 rules_evaluated += 1;
265 let rule_start = Instant::now();
266
267 if self.config.debug_mode {
268 println!("📝 Evaluating rule: {}", rule.name);
269 }
270
271 if self.evaluate_conditions(&rule.conditions, facts)? {
273 if self.config.debug_mode {
274 println!(
275 "🔥 Rule '{}' fired (salience: {})",
276 rule.name, rule.salience
277 );
278 }
279
280 for action in &rule.actions {
282 self.execute_action(action, facts)?;
283 }
284
285 let rule_duration = rule_start.elapsed();
286
287 if let Some(analytics) = &mut self.analytics {
289 analytics.record_execution(&rule.name, rule_duration, true, true, None, 0);
290 }
291
292 rules_fired += 1;
293 any_rule_fired = true;
294
295 fired_rules_in_cycle.insert(rule.name.clone());
297
298 if rule.no_loop {
300 self.fired_rules_global.insert(rule.name.clone());
301 }
302
303 self.agenda_manager.mark_rule_fired(rule);
305 self.activation_group_manager.mark_fired(rule);
306 } else {
307 let rule_duration = rule_start.elapsed();
308
309 if let Some(analytics) = &mut self.analytics {
311 analytics.record_execution(
312 &rule.name,
313 rule_duration,
314 false,
315 false,
316 None,
317 0,
318 );
319 }
320 }
321 }
322
323 if !any_rule_fired {
325 break;
326 }
327 }
328
329 let execution_time = start_time.elapsed();
330
331 Ok(GruleExecutionResult {
332 cycle_count,
333 rules_evaluated,
334 rules_fired,
335 execution_time,
336 })
337 }
338
339 fn evaluate_conditions(
341 &self,
342 conditions: &crate::engine::rule::ConditionGroup,
343 facts: &Facts,
344 ) -> Result<bool> {
345 use crate::engine::pattern_matcher::PatternMatcher;
346 use crate::engine::rule::ConditionGroup;
347
348 match conditions {
349 ConditionGroup::Single(condition) => self.evaluate_single_condition(condition, facts),
350 ConditionGroup::Compound {
351 left,
352 operator,
353 right,
354 } => {
355 let left_result = self.evaluate_conditions(left, facts)?;
356 let right_result = self.evaluate_conditions(right, facts)?;
357
358 match operator {
359 crate::types::LogicalOperator::And => Ok(left_result && right_result),
360 crate::types::LogicalOperator::Or => Ok(left_result || right_result),
361 crate::types::LogicalOperator::Not => Err(RuleEngineError::EvaluationError {
362 message: "NOT operator should not appear in compound conditions"
363 .to_string(),
364 }),
365 }
366 }
367 ConditionGroup::Not(condition) => {
368 let result = self.evaluate_conditions(condition, facts)?;
369 Ok(!result)
370 }
371 ConditionGroup::Exists(condition) => {
373 Ok(PatternMatcher::evaluate_exists(condition, facts))
374 }
375 ConditionGroup::Forall(condition) => {
376 Ok(PatternMatcher::evaluate_forall(condition, facts))
377 }
378 }
379 }
380
381 fn evaluate_single_condition(
383 &self,
384 condition: &crate::engine::rule::Condition,
385 facts: &Facts,
386 ) -> Result<bool> {
387 let field_value = facts.get_nested(&condition.field);
389
390 if field_value.is_none() {
391 return Ok(false); }
393
394 let field_value = field_value.unwrap();
395
396 Ok(condition.operator.evaluate(&field_value, &condition.value))
398 }
399
400 fn execute_action(&self, action: &ActionType, facts: &Facts) -> Result<()> {
402 match action {
403 ActionType::Set { field, value } => {
404 facts.set_nested(field, value.clone())?;
405 if self.config.debug_mode {
406 println!(" ✅ Set {field} = {value:?}");
407 }
408 }
409 ActionType::Log { message } => {
410 println!("📋 LOG: {}", message);
411 }
412 ActionType::Call { function, args } => {
413 let result = self.execute_function_call(function, args, facts)?;
414 if self.config.debug_mode {
415 println!(" 📞 Called {function}({args:?}) -> {result}");
416 }
417 }
418 ActionType::MethodCall {
419 object,
420 method,
421 args,
422 } => {
423 let result = self.execute_method_call(object, method, args, facts)?;
424 if self.config.debug_mode {
425 println!(" 🔧 Called {object}.{method}({args:?}) -> {result}");
426 }
427 }
428 ActionType::Update { object } => {
429 if self.config.debug_mode {
430 println!(" 🔄 Updated {object}");
431 }
432 }
435 ActionType::Custom {
436 action_type,
437 params,
438 } => {
439 if let Some(handler) = self.action_handlers.get(action_type) {
440 if self.config.debug_mode {
441 println!(" 🎯 Executing custom action: {action_type} with params: {params:?}");
442 }
443
444 let resolved_params = self.resolve_action_parameters(params, facts)?;
446
447 handler(&resolved_params, facts)?;
449 } else {
450 if self.config.debug_mode {
451 println!(" ⚠️ No handler registered for custom action: {action_type}");
452 println!(" Available handlers: {:?}", self.action_handlers.keys().collect::<Vec<_>>());
453 }
454
455 return Err(RuleEngineError::EvaluationError {
457 message: format!(
458 "No action handler registered for '{action_type}'. Use engine.register_action_handler() to add custom action handlers."
459 ),
460 });
461 }
462 }
463 }
464 Ok(())
465 }
466
467 fn execute_function_call(
469 &self,
470 function: &str,
471 args: &[Value],
472 facts: &Facts,
473 ) -> Result<String> {
474 let function_lower = function.to_lowercase();
475
476 match function_lower.as_str() {
478 "log" | "print" | "println" => self.handle_log_function(args),
479 "update" | "refresh" => self.handle_update_function(args),
480 "now" | "timestamp" => self.handle_timestamp_function(),
481 "random" => self.handle_random_function(args),
482 "format" | "sprintf" => self.handle_format_function(args),
483 "length" | "size" | "count" => self.handle_length_function(args),
484 "sum" | "add" => self.handle_sum_function(args),
485 "max" | "maximum" => self.handle_max_function(args),
486 "min" | "minimum" => self.handle_min_function(args),
487 "avg" | "average" => self.handle_average_function(args),
488 "round" => self.handle_round_function(args),
489 "floor" => self.handle_floor_function(args),
490 "ceil" | "ceiling" => self.handle_ceil_function(args),
491 "abs" | "absolute" => self.handle_abs_function(args),
492 "contains" | "includes" => self.handle_contains_function(args),
493 "startswith" | "begins_with" => self.handle_starts_with_function(args),
494 "endswith" | "ends_with" => self.handle_ends_with_function(args),
495 "lowercase" | "tolower" => self.handle_lowercase_function(args),
496 "uppercase" | "toupper" => self.handle_uppercase_function(args),
497 "trim" | "strip" => self.handle_trim_function(args),
498 "split" => self.handle_split_function(args),
499 "join" => self.handle_join_function(args),
500 _ => {
501 self.handle_custom_function(function, args, facts)
503 }
504 }
505 }
506
507 fn handle_log_function(&self, args: &[Value]) -> Result<String> {
509 let message = if args.is_empty() {
510 "".to_string()
511 } else if args.len() == 1 {
512 args[0].to_string()
513 } else {
514 args.iter()
515 .map(|v| v.to_string())
516 .collect::<Vec<_>>()
517 .join(" ")
518 };
519
520 println!("📋 {}", message);
521 Ok(message)
522 }
523
524 fn handle_update_function(&self, args: &[Value]) -> Result<String> {
526 if let Some(arg) = args.first() {
527 Ok(format!("Updated: {}", arg.to_string()))
528 } else {
529 Ok("Updated".to_string())
530 }
531 }
532
533 fn handle_timestamp_function(&self) -> Result<String> {
535 use std::time::{SystemTime, UNIX_EPOCH};
536 let timestamp = SystemTime::now()
537 .duration_since(UNIX_EPOCH)
538 .map_err(|e| RuleEngineError::EvaluationError {
539 message: format!("Failed to get timestamp: {}", e),
540 })?
541 .as_secs();
542 Ok(timestamp.to_string())
543 }
544
545 fn handle_random_function(&self, args: &[Value]) -> Result<String> {
547 use std::collections::hash_map::DefaultHasher;
548 use std::hash::{Hash, Hasher};
549
550 let mut hasher = DefaultHasher::new();
552 std::time::SystemTime::now().hash(&mut hasher);
553 let random_value = hasher.finish();
554
555 if args.is_empty() {
556 Ok((random_value % 100).to_string()) } else if let Some(Value::Number(max)) = args.first() {
558 let max_val = *max as u64;
559 Ok((random_value % max_val).to_string())
560 } else {
561 Ok(random_value.to_string())
562 }
563 }
564
565 fn handle_format_function(&self, args: &[Value]) -> Result<String> {
567 if args.is_empty() {
568 return Ok("".to_string());
569 }
570
571 let template = args[0].to_string();
572 let values: Vec<String> = args[1..].iter().map(|v| v.to_string()).collect();
573
574 let mut result = template;
576 for (i, value) in values.iter().enumerate() {
577 result = result.replace(&format!("{{{}}}", i), value);
578 }
579
580 Ok(result)
581 }
582
583 fn handle_length_function(&self, args: &[Value]) -> Result<String> {
585 if let Some(arg) = args.first() {
586 match arg {
587 Value::String(s) => Ok(s.len().to_string()),
588 Value::Array(arr) => Ok(arr.len().to_string()),
589 Value::Object(obj) => Ok(obj.len().to_string()),
590 _ => Ok("1".to_string()), }
592 } else {
593 Ok("0".to_string())
594 }
595 }
596
597 fn handle_sum_function(&self, args: &[Value]) -> Result<String> {
599 let sum = args.iter().fold(0.0, |acc, val| match val {
600 Value::Number(n) => acc + n,
601 Value::Integer(i) => acc + (*i as f64),
602 _ => acc,
603 });
604 Ok(sum.to_string())
605 }
606
607 fn handle_max_function(&self, args: &[Value]) -> Result<String> {
609 let max = args.iter().fold(f64::NEG_INFINITY, |acc, val| match val {
610 Value::Number(n) => acc.max(*n),
611 Value::Integer(i) => acc.max(*i as f64),
612 _ => acc,
613 });
614 Ok(max.to_string())
615 }
616
617 fn handle_min_function(&self, args: &[Value]) -> Result<String> {
619 let min = args.iter().fold(f64::INFINITY, |acc, val| match val {
620 Value::Number(n) => acc.min(*n),
621 Value::Integer(i) => acc.min(*i as f64),
622 _ => acc,
623 });
624 Ok(min.to_string())
625 }
626
627 fn handle_average_function(&self, args: &[Value]) -> Result<String> {
629 if args.is_empty() {
630 return Ok("0".to_string());
631 }
632
633 let (sum, count) = args.iter().fold((0.0, 0), |(sum, count), val| match val {
634 Value::Number(n) => (sum + n, count + 1),
635 Value::Integer(i) => (sum + (*i as f64), count + 1),
636 _ => (sum, count),
637 });
638
639 if count > 0 {
640 Ok((sum / count as f64).to_string())
641 } else {
642 Ok("0".to_string())
643 }
644 }
645
646 fn handle_round_function(&self, args: &[Value]) -> Result<String> {
648 if let Some(Value::Number(n)) = args.first() {
649 Ok(n.round().to_string())
650 } else if let Some(Value::Integer(i)) = args.first() {
651 Ok(i.to_string())
652 } else {
653 Err(RuleEngineError::EvaluationError {
654 message: "round() requires a numeric argument".to_string(),
655 })
656 }
657 }
658
659 fn handle_floor_function(&self, args: &[Value]) -> Result<String> {
660 if let Some(Value::Number(n)) = args.first() {
661 Ok(n.floor().to_string())
662 } else if let Some(Value::Integer(i)) = args.first() {
663 Ok(i.to_string())
664 } else {
665 Err(RuleEngineError::EvaluationError {
666 message: "floor() requires a numeric argument".to_string(),
667 })
668 }
669 }
670
671 fn handle_ceil_function(&self, args: &[Value]) -> Result<String> {
672 if let Some(Value::Number(n)) = args.first() {
673 Ok(n.ceil().to_string())
674 } else if let Some(Value::Integer(i)) = args.first() {
675 Ok(i.to_string())
676 } else {
677 Err(RuleEngineError::EvaluationError {
678 message: "ceil() requires a numeric argument".to_string(),
679 })
680 }
681 }
682
683 fn handle_abs_function(&self, args: &[Value]) -> Result<String> {
684 if let Some(Value::Number(n)) = args.first() {
685 Ok(n.abs().to_string())
686 } else if let Some(Value::Integer(i)) = args.first() {
687 Ok(i.abs().to_string())
688 } else {
689 Err(RuleEngineError::EvaluationError {
690 message: "abs() requires a numeric argument".to_string(),
691 })
692 }
693 }
694
695 fn handle_contains_function(&self, args: &[Value]) -> Result<String> {
697 if args.len() >= 2 {
698 let haystack = args[0].to_string();
699 let needle = args[1].to_string();
700 Ok(haystack.contains(&needle).to_string())
701 } else {
702 Err(RuleEngineError::EvaluationError {
703 message: "contains() requires 2 arguments".to_string(),
704 })
705 }
706 }
707
708 fn handle_starts_with_function(&self, args: &[Value]) -> Result<String> {
709 if args.len() >= 2 {
710 let text = args[0].to_string();
711 let prefix = args[1].to_string();
712 Ok(text.starts_with(&prefix).to_string())
713 } else {
714 Err(RuleEngineError::EvaluationError {
715 message: "startswith() requires 2 arguments".to_string(),
716 })
717 }
718 }
719
720 fn handle_ends_with_function(&self, args: &[Value]) -> Result<String> {
721 if args.len() >= 2 {
722 let text = args[0].to_string();
723 let suffix = args[1].to_string();
724 Ok(text.ends_with(&suffix).to_string())
725 } else {
726 Err(RuleEngineError::EvaluationError {
727 message: "endswith() requires 2 arguments".to_string(),
728 })
729 }
730 }
731
732 fn handle_lowercase_function(&self, args: &[Value]) -> Result<String> {
733 if let Some(arg) = args.first() {
734 Ok(arg.to_string().to_lowercase())
735 } else {
736 Err(RuleEngineError::EvaluationError {
737 message: "lowercase() requires 1 argument".to_string(),
738 })
739 }
740 }
741
742 fn handle_uppercase_function(&self, args: &[Value]) -> Result<String> {
743 if let Some(arg) = args.first() {
744 Ok(arg.to_string().to_uppercase())
745 } else {
746 Err(RuleEngineError::EvaluationError {
747 message: "uppercase() requires 1 argument".to_string(),
748 })
749 }
750 }
751
752 fn handle_trim_function(&self, args: &[Value]) -> Result<String> {
753 if let Some(arg) = args.first() {
754 Ok(arg.to_string().trim().to_string())
755 } else {
756 Err(RuleEngineError::EvaluationError {
757 message: "trim() requires 1 argument".to_string(),
758 })
759 }
760 }
761
762 fn handle_split_function(&self, args: &[Value]) -> Result<String> {
763 if args.len() >= 2 {
764 let text = args[0].to_string();
765 let delimiter = args[1].to_string();
766 let parts: Vec<String> = text.split(&delimiter).map(|s| s.to_string()).collect();
767 Ok(format!("{:?}", parts)) } else {
769 Err(RuleEngineError::EvaluationError {
770 message: "split() requires 2 arguments".to_string(),
771 })
772 }
773 }
774
775 fn handle_join_function(&self, args: &[Value]) -> Result<String> {
776 if args.len() >= 2 {
777 let delimiter = args[0].to_string();
778 let parts: Vec<String> = args[1..].iter().map(|v| v.to_string()).collect();
779 Ok(parts.join(&delimiter))
780 } else {
781 Err(RuleEngineError::EvaluationError {
782 message: "join() requires at least 2 arguments".to_string(),
783 })
784 }
785 }
786
787 fn handle_custom_function(
789 &self,
790 function: &str,
791 args: &[Value],
792 facts: &Facts,
793 ) -> Result<String> {
794 if let Some(custom_func) = self.custom_functions.get(function) {
796 if self.config.debug_mode {
797 println!("🎯 Calling registered function: {}({:?})", function, args);
798 }
799
800 match custom_func(args, facts) {
801 Ok(result) => Ok(result.to_string()),
802 Err(e) => Err(e),
803 }
804 } else {
805 if self.config.debug_mode {
807 println!("⚠️ Custom function '{}' not registered", function);
808 }
809
810 Err(RuleEngineError::EvaluationError {
811 message: format!("Function '{}' is not registered. Use engine.register_function() to add custom functions.", function),
812 })
813 }
814 }
815
816 fn execute_method_call(
818 &self,
819 object_name: &str,
820 method: &str,
821 args: &[Value],
822 facts: &Facts,
823 ) -> Result<String> {
824 let Some(object_value) = facts.get(object_name) else {
826 return Err(RuleEngineError::EvaluationError {
827 message: format!("Object '{}' not found in facts", object_name),
828 });
829 };
830
831 let method_lower = method.to_lowercase();
832
833 if method_lower.starts_with("set") && args.len() == 1 {
835 return self.handle_setter_method(object_name, method, &args[0], object_value, facts);
836 }
837
838 if method_lower.starts_with("get") && args.is_empty() {
840 return self.handle_getter_method(object_name, method, &object_value);
841 }
842
843 match method_lower.as_str() {
845 "tostring" => Ok(object_value.to_string()),
846 "update" => {
847 facts.add_value(object_name, object_value)?;
848 Ok(format!("Updated {}", object_name))
849 }
850 "reset" => self.handle_reset_method(object_name, object_value, facts),
851 _ => self.handle_property_access_or_fallback(
852 object_name,
853 method,
854 args.len(),
855 &object_value,
856 ),
857 }
858 }
859
860 fn handle_setter_method(
862 &self,
863 object_name: &str,
864 method: &str,
865 new_value: &Value,
866 mut object_value: Value,
867 facts: &Facts,
868 ) -> Result<String> {
869 let property_name = Self::extract_property_name_from_setter(method);
870
871 match object_value {
872 Value::Object(ref mut obj) => {
873 obj.insert(property_name.clone(), new_value.clone());
874 facts.add_value(object_name, object_value)?;
875 Ok(format!(
876 "Set {} to {}",
877 property_name,
878 new_value.to_string()
879 ))
880 }
881 _ => Err(RuleEngineError::EvaluationError {
882 message: format!("Cannot call setter on non-object type: {}", object_name),
883 }),
884 }
885 }
886
887 fn handle_getter_method(
889 &self,
890 object_name: &str,
891 method: &str,
892 object_value: &Value,
893 ) -> Result<String> {
894 let property_name = Self::extract_property_name_from_getter(method);
895
896 match object_value {
897 Value::Object(obj) => {
898 if let Some(value) = obj.get(&property_name) {
899 Ok(value.to_string())
900 } else {
901 Err(RuleEngineError::EvaluationError {
902 message: format!(
903 "Property '{}' not found on object '{}'",
904 property_name, object_name
905 ),
906 })
907 }
908 }
909 _ => Err(RuleEngineError::EvaluationError {
910 message: format!("Cannot call getter on non-object type: {}", object_name),
911 }),
912 }
913 }
914
915 fn handle_reset_method(
917 &self,
918 object_name: &str,
919 mut object_value: Value,
920 facts: &Facts,
921 ) -> Result<String> {
922 match object_value {
923 Value::Object(ref mut obj) => {
924 obj.clear();
925 facts.add_value(object_name, object_value)?;
926 Ok(format!("Reset {}", object_name))
927 }
928 _ => Err(RuleEngineError::EvaluationError {
929 message: format!("Cannot reset non-object type: {}", object_name),
930 }),
931 }
932 }
933
934 fn handle_property_access_or_fallback(
936 &self,
937 object_name: &str,
938 method: &str,
939 arg_count: usize,
940 object_value: &Value,
941 ) -> Result<String> {
942 if let Value::Object(obj) = object_value {
943 if let Some(value) = obj.get(method) {
945 return Ok(value.to_string());
946 }
947
948 let capitalized_method = Self::capitalize_first_letter(method);
950 if let Some(value) = obj.get(&capitalized_method) {
951 return Ok(value.to_string());
952 }
953 }
954
955 Ok(format!(
957 "Called {}.{} with {} args",
958 object_name, method, arg_count
959 ))
960 }
961
962 fn extract_property_name_from_setter(method: &str) -> String {
964 let property_name = &method[3..]; Self::capitalize_first_letter(property_name)
966 }
967
968 fn extract_property_name_from_getter(method: &str) -> String {
970 let property_name = &method[3..]; Self::capitalize_first_letter(property_name)
972 }
973
974 fn capitalize_first_letter(s: &str) -> String {
976 if s.is_empty() {
977 return String::new();
978 }
979 let mut chars = s.chars();
980 match chars.next() {
981 None => String::new(),
982 Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
983 }
984 }
985
986 fn resolve_action_parameters(
988 &self,
989 params: &HashMap<String, Value>,
990 facts: &Facts,
991 ) -> Result<HashMap<String, Value>> {
992 let mut resolved = HashMap::new();
993
994 for (key, value) in params {
995 let resolved_value = match value {
996 Value::String(s) => {
997 if s.contains('.') {
999 if let Some(fact_value) = facts.get_nested(s) {
1001 fact_value
1002 } else {
1003 value.clone()
1005 }
1006 } else {
1007 value.clone()
1008 }
1009 }
1010 _ => value.clone(),
1011 };
1012 resolved.insert(key.clone(), resolved_value);
1013 }
1014
1015 Ok(resolved)
1016 }
1017}