1use crate::engine::{
2 agenda::{ActivationGroupManager, AgendaManager},
3 analytics::RuleAnalytics,
4 facts::Facts,
5 knowledge_base::KnowledgeBase,
6 workflow::WorkflowEngine,
7};
8use crate::errors::{Result, RuleEngineError};
9use crate::types::{ActionType, Value};
10use chrono::{DateTime, Utc};
11use std::collections::HashMap;
12use std::time::{Duration, Instant};
13
14pub type CustomFunction = Box<dyn Fn(&[Value], &Facts) -> Result<Value> + Send + Sync>;
16
17pub type ActionHandler = Box<dyn Fn(&HashMap<String, Value>, &Facts) -> Result<()> + Send + Sync>;
19
20#[derive(Debug, Clone)]
22pub struct EngineConfig {
23 pub max_cycles: usize,
25 pub timeout: Option<Duration>,
27 pub enable_stats: bool,
29 pub debug_mode: bool,
31}
32
33impl Default for EngineConfig {
34 fn default() -> Self {
35 Self {
36 max_cycles: 100,
37 timeout: Some(Duration::from_secs(30)),
38 enable_stats: true,
39 debug_mode: false,
40 }
41 }
42}
43
44#[derive(Debug, Clone)]
46pub struct GruleExecutionResult {
47 pub cycle_count: usize,
49 pub rules_evaluated: usize,
51 pub rules_fired: usize,
53 pub execution_time: Duration,
55}
56
57pub struct RustRuleEngine {
59 knowledge_base: KnowledgeBase,
60 config: EngineConfig,
61 custom_functions: HashMap<String, CustomFunction>,
62 action_handlers: HashMap<String, ActionHandler>,
63 analytics: Option<RuleAnalytics>,
64 agenda_manager: AgendaManager,
65 activation_group_manager: ActivationGroupManager,
66 fired_rules_global: std::collections::HashSet<String>,
68 workflow_engine: WorkflowEngine,
70}
71
72impl RustRuleEngine {
73 pub fn new(knowledge_base: KnowledgeBase) -> Self {
75 Self {
76 knowledge_base,
77 config: EngineConfig::default(),
78 custom_functions: HashMap::new(),
79 action_handlers: HashMap::new(),
80 analytics: None,
81 agenda_manager: AgendaManager::new(),
82 activation_group_manager: ActivationGroupManager::new(),
83 fired_rules_global: std::collections::HashSet::new(),
84 workflow_engine: WorkflowEngine::new(),
85 }
86 }
87
88 pub fn with_config(knowledge_base: KnowledgeBase, config: EngineConfig) -> Self {
90 Self {
91 knowledge_base,
92 config,
93 custom_functions: HashMap::new(),
94 action_handlers: HashMap::new(),
95 analytics: None,
96 agenda_manager: AgendaManager::new(),
97 activation_group_manager: ActivationGroupManager::new(),
98 fired_rules_global: std::collections::HashSet::new(),
99 workflow_engine: WorkflowEngine::new(),
100 }
101 }
102
103 pub fn register_function<F>(&mut self, name: &str, func: F)
105 where
106 F: Fn(&[Value], &Facts) -> Result<Value> + Send + Sync + 'static,
107 {
108 self.custom_functions
109 .insert(name.to_string(), Box::new(func));
110 }
111
112 pub fn register_action_handler<F>(&mut self, action_type: &str, handler: F)
114 where
115 F: Fn(&HashMap<String, Value>, &Facts) -> Result<()> + Send + Sync + 'static,
116 {
117 self.action_handlers
118 .insert(action_type.to_string(), Box::new(handler));
119 }
120
121 pub fn enable_analytics(&mut self, analytics: RuleAnalytics) {
123 self.analytics = Some(analytics);
124 }
125
126 pub fn reset_no_loop_tracking(&mut self) {
128 self.fired_rules_global.clear();
129 }
130
131 pub fn disable_analytics(&mut self) {
133 self.analytics = None;
134 }
135
136 pub fn analytics(&self) -> Option<&RuleAnalytics> {
138 self.analytics.as_ref()
139 }
140
141 pub fn has_function(&self, name: &str) -> bool {
143 self.custom_functions.contains_key(name)
144 }
145
146 pub fn has_action_handler(&self, action_type: &str) -> bool {
148 self.action_handlers.contains_key(action_type)
149 }
150
151 pub fn get_ready_tasks(&mut self) -> Vec<crate::engine::workflow::ScheduledTask> {
153 self.workflow_engine.get_ready_tasks()
154 }
155
156 pub fn execute_scheduled_tasks(&mut self, facts: &Facts) -> Result<()> {
158 let ready_tasks = self.get_ready_tasks();
159 for task in ready_tasks {
160 if let Some(rule) = self.knowledge_base.get_rules().iter().find(|r| r.name == task.rule_name) {
161 if self.config.debug_mode {
162 println!("β‘ Executing scheduled task: {}", task.rule_name);
163 }
164
165 if self.evaluate_conditions(&rule.conditions, facts)? {
167 for action in &rule.actions {
168 self.execute_action(action, facts)?;
169 }
170 }
171 }
172 }
173 Ok(())
174 }
175
176 pub fn activate_agenda_group(&mut self, group: String) {
178 self.workflow_engine.activate_agenda_group(group.clone());
179 self.agenda_manager.set_focus(&group);
180 }
181
182 pub fn knowledge_base(&self) -> &KnowledgeBase {
184 &self.knowledge_base
185 }
186
187 pub fn knowledge_base_mut(&mut self) -> &mut KnowledgeBase {
189 &mut self.knowledge_base
190 }
191
192 fn sync_workflow_agenda_activations(&mut self) {
194 while let Some(agenda_group) = self.workflow_engine.get_next_pending_agenda_activation() {
196 if self.config.debug_mode {
197 println!("π Syncing workflow agenda activation: {}", agenda_group);
198 }
199 self.agenda_manager.set_focus(&agenda_group);
200 }
201 }
202
203 pub fn set_agenda_focus(&mut self, group: &str) {
205 self.agenda_manager.set_focus(group);
206 }
207
208 pub fn get_active_agenda_group(&self) -> &str {
210 self.agenda_manager.get_active_group()
211 }
212
213 pub fn pop_agenda_focus(&mut self) -> Option<String> {
215 self.agenda_manager.pop_focus()
216 }
217
218 pub fn clear_agenda_focus(&mut self) {
220 self.agenda_manager.clear_focus();
221 }
222
223 pub fn get_agenda_groups(&self) -> Vec<String> {
225 self.agenda_manager
226 .get_agenda_groups(&self.knowledge_base.get_rules())
227 }
228
229 pub fn get_activation_groups(&self) -> Vec<String> {
231 self.activation_group_manager
232 .get_activation_groups(&self.knowledge_base.get_rules())
233 }
234
235 pub fn start_workflow(&mut self, workflow_name: Option<String>) -> String {
239 self.workflow_engine.start_workflow(workflow_name)
240 }
241
242 pub fn get_workflow_stats(&self) -> crate::engine::workflow::WorkflowStats {
244 self.workflow_engine.get_workflow_stats()
245 }
246
247 pub fn get_workflow(&self, workflow_id: &str) -> Option<&crate::engine::workflow::WorkflowState> {
249 self.workflow_engine.get_workflow(workflow_id)
250 }
251
252 pub fn cleanup_completed_workflows(&mut self, older_than: Duration) {
254 self.workflow_engine.cleanup_completed_workflows(older_than);
255 }
256
257 pub fn execute_workflow_step(&mut self, agenda_group: &str, facts: &Facts) -> Result<GruleExecutionResult> {
259 self.set_agenda_focus(agenda_group);
261
262 let result = self.execute(facts)?;
264
265 self.process_workflow_actions(facts)?;
267
268 Ok(result)
269 }
270
271 pub fn execute_workflow(&mut self, agenda_groups: Vec<&str>, facts: &Facts) -> Result<crate::engine::workflow::WorkflowResult> {
273 let start_time = Instant::now();
274 let mut total_steps = 0;
275
276 if self.config.debug_mode {
277 println!("π Starting workflow execution with {} steps", agenda_groups.len());
278 }
279
280 for (i, group) in agenda_groups.iter().enumerate() {
281 if self.config.debug_mode {
282 println!("π Executing workflow step {}: {}", i + 1, group);
283 }
284
285 let step_result = self.execute_workflow_step(group, facts)?;
286 total_steps += 1;
287
288 if step_result.rules_fired == 0 {
289 if self.config.debug_mode {
290 println!("βΈοΈ No rules fired in step '{}', stopping workflow", group);
291 }
292 break;
293 }
294 }
295
296 let execution_time = start_time.elapsed();
297
298 Ok(crate::engine::workflow::WorkflowResult::success(total_steps, execution_time))
299 }
300
301 fn process_workflow_actions(&mut self, facts: &Facts) -> Result<()> {
303 while let Some(group) = self.workflow_engine.get_next_agenda_group() {
305 self.set_agenda_focus(&group);
306 }
307
308 let ready_tasks = self.workflow_engine.get_ready_tasks();
310 for task in ready_tasks {
311 if self.config.debug_mode {
312 println!("β‘ Executing scheduled task: {}", task.rule_name);
313 }
314
315 if let Some(rule) = self.knowledge_base.get_rules().iter().find(|r| r.name == task.rule_name) {
317 if self.evaluate_conditions(&rule.conditions, facts)? {
319 for action in &rule.actions {
320 self.execute_action(action, facts)?;
321 }
322 }
323 }
324 }
325
326 Ok(())
327 }
328
329 pub fn execute(&mut self, facts: &Facts) -> Result<GruleExecutionResult> {
331 self.execute_at_time(facts, Utc::now())
332 }
333
334 pub fn execute_at_time(
336 &mut self,
337 facts: &Facts,
338 timestamp: DateTime<Utc>,
339 ) -> Result<GruleExecutionResult> {
340 let start_time = Instant::now();
341 let mut cycle_count = 0;
342 let mut rules_evaluated = 0;
343 let mut rules_fired = 0;
344
345 self.sync_workflow_agenda_activations();
347
348 if self.config.debug_mode {
349 println!(
350 "π Starting rule execution with {} rules (agenda group: {})",
351 self.knowledge_base.get_rules().len(),
352 self.agenda_manager.get_active_group()
353 );
354 }
355
356 for cycle in 0..self.config.max_cycles {
357 cycle_count = cycle + 1;
358 let mut any_rule_fired = false;
359 let mut fired_rules_in_cycle = std::collections::HashSet::new();
360
361 self.activation_group_manager.reset_cycle();
363
364 if let Some(timeout) = self.config.timeout {
366 if start_time.elapsed() > timeout {
367 return Err(RuleEngineError::EvaluationError {
368 message: "Execution timeout exceeded".to_string(),
369 });
370 }
371 }
372
373 let mut rules = self.knowledge_base.get_rules().clone();
375 rules.sort_by(|a, b| b.salience.cmp(&a.salience));
376
377 let rules: Vec<_> = rules
379 .iter()
380 .filter(|rule| self.agenda_manager.should_evaluate_rule(rule))
381 .collect();
382
383 for rule in &rules {
384 if !rule.enabled {
385 continue;
386 }
387
388 if !rule.is_active_at(timestamp) {
390 continue;
391 }
392
393 if !self.agenda_manager.can_fire_rule(rule) {
395 continue;
396 }
397
398 if !self.activation_group_manager.can_fire(rule) {
400 continue;
401 }
402
403 if rule.no_loop && self.fired_rules_global.contains(&rule.name) {
405 continue;
406 }
407
408 rules_evaluated += 1;
409 let rule_start = Instant::now();
410
411 if self.config.debug_mode {
412 println!("π Evaluating rule: {}", rule.name);
413 }
414
415 let condition_result = self.evaluate_conditions(&rule.conditions, facts)?;
417 if self.config.debug_mode {
418 println!(" π Condition result for '{}': {}", rule.name, condition_result);
419 }
420
421 if condition_result {
422 if self.config.debug_mode {
423 println!(
424 "π₯ Rule '{}' fired (salience: {})",
425 rule.name, rule.salience
426 );
427 }
428
429 for action in &rule.actions {
431 self.execute_action(action, facts)?;
432 }
433
434 let rule_duration = rule_start.elapsed();
435
436 if let Some(analytics) = &mut self.analytics {
438 analytics.record_execution(&rule.name, rule_duration, true, true, None, 0);
439 }
440
441 rules_fired += 1;
442 any_rule_fired = true;
443
444 fired_rules_in_cycle.insert(rule.name.clone());
446
447 if rule.no_loop {
449 self.fired_rules_global.insert(rule.name.clone());
450 }
451
452 self.agenda_manager.mark_rule_fired(rule);
454 self.activation_group_manager.mark_fired(rule);
455 } else {
456 let rule_duration = rule_start.elapsed();
457
458 if let Some(analytics) = &mut self.analytics {
460 analytics.record_execution(
461 &rule.name,
462 rule_duration,
463 false,
464 false,
465 None,
466 0,
467 );
468 }
469 }
470 }
471
472 if !any_rule_fired {
474 break;
475 }
476
477 self.sync_workflow_agenda_activations();
479 }
480
481 let execution_time = start_time.elapsed();
482
483 Ok(GruleExecutionResult {
484 cycle_count,
485 rules_evaluated,
486 rules_fired,
487 execution_time,
488 })
489 }
490
491 fn evaluate_conditions(
493 &self,
494 conditions: &crate::engine::rule::ConditionGroup,
495 facts: &Facts,
496 ) -> Result<bool> {
497 use crate::engine::pattern_matcher::PatternMatcher;
498 use crate::engine::rule::ConditionGroup;
499
500 match conditions {
501 ConditionGroup::Single(condition) => self.evaluate_single_condition(condition, facts),
502 ConditionGroup::Compound {
503 left,
504 operator,
505 right,
506 } => {
507 let left_result = self.evaluate_conditions(left, facts)?;
508 let right_result = self.evaluate_conditions(right, facts)?;
509
510 match operator {
511 crate::types::LogicalOperator::And => Ok(left_result && right_result),
512 crate::types::LogicalOperator::Or => Ok(left_result || right_result),
513 crate::types::LogicalOperator::Not => Err(RuleEngineError::EvaluationError {
514 message: "NOT operator should not appear in compound conditions"
515 .to_string(),
516 }),
517 }
518 }
519 ConditionGroup::Not(condition) => {
520 let result = self.evaluate_conditions(condition, facts)?;
521 Ok(!result)
522 }
523 ConditionGroup::Exists(condition) => {
525 Ok(PatternMatcher::evaluate_exists(condition, facts))
526 }
527 ConditionGroup::Forall(condition) => {
528 Ok(PatternMatcher::evaluate_forall(condition, facts))
529 }
530 }
531 }
532
533 fn evaluate_rule_conditions(&self, rule: &crate::engine::rule::Rule, facts: &Facts) -> Result<bool> {
535 self.evaluate_conditions(&rule.conditions, facts)
536 }
537
538 fn evaluate_single_condition(
540 &self,
541 condition: &crate::engine::rule::Condition,
542 facts: &Facts,
543 ) -> Result<bool> {
544 let field_value = facts.get_nested(&condition.field)
546 .or_else(|| facts.get(&condition.field));
547
548 if self.config.debug_mode {
549 println!(" π Evaluating condition: {} {} {:?}",
550 condition.field,
551 format!("{:?}", condition.operator).to_lowercase(),
552 condition.value
553 );
554 println!(" Field value: {:?}", field_value);
555 }
556
557 if field_value.is_none() {
558 return Ok(false); }
560
561 let field_value = field_value.unwrap();
562
563 Ok(condition.operator.evaluate(&field_value, &condition.value))
565 }
566
567 fn execute_action(&mut self, action: &ActionType, facts: &Facts) -> Result<()> {
569 match action {
570 ActionType::Set { field, value } => {
571 if let Err(_) = facts.set_nested(field, value.clone()) {
573 facts.set(field, value.clone());
575 }
576 if self.config.debug_mode {
577 println!(" β
Set {field} = {value:?}");
578 }
579 }
580 ActionType::Log { message } => {
581 println!("π LOG: {}", message);
582 }
583 ActionType::Call { function, args } => {
584 let result = self.execute_function_call(function, args, facts)?;
585 if self.config.debug_mode {
586 println!(" π Called {function}({args:?}) -> {result}");
587 }
588 }
589 ActionType::MethodCall {
590 object,
591 method,
592 args,
593 } => {
594 let result = self.execute_method_call(object, method, args, facts)?;
595 if self.config.debug_mode {
596 println!(" π§ Called {object}.{method}({args:?}) -> {result}");
597 }
598 }
599 ActionType::Update { object } => {
600 if self.config.debug_mode {
601 println!(" π Updated {object}");
602 }
603 }
606 ActionType::Custom {
607 action_type,
608 params,
609 } => {
610 if let Some(handler) = self.action_handlers.get(action_type) {
611 if self.config.debug_mode {
612 println!(" π― Executing custom action: {action_type} with params: {params:?}");
613 }
614
615 let resolved_params = self.resolve_action_parameters(params, facts)?;
617
618 handler(&resolved_params, facts)?;
620 } else {
621 if self.config.debug_mode {
622 println!(" β οΈ No handler registered for custom action: {action_type}");
623 println!(" Available handlers: {:?}", self.action_handlers.keys().collect::<Vec<_>>());
624 }
625
626 return Err(RuleEngineError::EvaluationError {
628 message: format!(
629 "No action handler registered for '{action_type}'. Use engine.register_action_handler() to add custom action handlers."
630 ),
631 });
632 }
633 }
634 ActionType::ActivateAgendaGroup { group } => {
636 if self.config.debug_mode {
637 println!(" π― Activating agenda group: {}", group);
638 }
639 self.workflow_engine.activate_agenda_group(group.clone());
641 self.agenda_manager.set_focus(group);
642 }
643 ActionType::ScheduleRule { rule_name, delay_ms } => {
644 if self.config.debug_mode {
645 println!(" β° Scheduling rule '{}' to execute in {}ms", rule_name, delay_ms);
646 }
647 self.workflow_engine.schedule_rule(rule_name.clone(), *delay_ms, None);
648 }
649 ActionType::CompleteWorkflow { workflow_name } => {
650 if self.config.debug_mode {
651 println!(" β
Completing workflow: {}", workflow_name);
652 }
653 self.workflow_engine.complete_workflow(workflow_name.clone());
654 }
655 ActionType::SetWorkflowData { key, value } => {
656 if self.config.debug_mode {
657 println!(" πΎ Setting workflow data: {} = {:?}", key, value);
658 }
659 let workflow_id = "default_workflow";
662 self.workflow_engine.set_workflow_data(workflow_id, key.clone(), value.clone());
663 }
664 }
665 Ok(())
666 }
667
668 fn execute_function_call(
670 &self,
671 function: &str,
672 args: &[Value],
673 facts: &Facts,
674 ) -> Result<String> {
675 let function_lower = function.to_lowercase();
676
677 match function_lower.as_str() {
679 "log" | "print" | "println" => self.handle_log_function(args),
680 "update" | "refresh" => self.handle_update_function(args),
681 "now" | "timestamp" => self.handle_timestamp_function(),
682 "random" => self.handle_random_function(args),
683 "format" | "sprintf" => self.handle_format_function(args),
684 "length" | "size" | "count" => self.handle_length_function(args),
685 "sum" | "add" => self.handle_sum_function(args),
686 "max" | "maximum" => self.handle_max_function(args),
687 "min" | "minimum" => self.handle_min_function(args),
688 "avg" | "average" => self.handle_average_function(args),
689 "round" => self.handle_round_function(args),
690 "floor" => self.handle_floor_function(args),
691 "ceil" | "ceiling" => self.handle_ceil_function(args),
692 "abs" | "absolute" => self.handle_abs_function(args),
693 "contains" | "includes" => self.handle_contains_function(args),
694 "startswith" | "begins_with" => self.handle_starts_with_function(args),
695 "endswith" | "ends_with" => self.handle_ends_with_function(args),
696 "lowercase" | "tolower" => self.handle_lowercase_function(args),
697 "uppercase" | "toupper" => self.handle_uppercase_function(args),
698 "trim" | "strip" => self.handle_trim_function(args),
699 "split" => self.handle_split_function(args),
700 "join" => self.handle_join_function(args),
701 _ => {
702 self.handle_custom_function(function, args, facts)
704 }
705 }
706 }
707
708 fn handle_log_function(&self, args: &[Value]) -> Result<String> {
710 let message = if args.is_empty() {
711 "".to_string()
712 } else if args.len() == 1 {
713 args[0].to_string()
714 } else {
715 args.iter()
716 .map(|v| v.to_string())
717 .collect::<Vec<_>>()
718 .join(" ")
719 };
720
721 println!("π {}", message);
722 Ok(message)
723 }
724
725 fn handle_update_function(&self, args: &[Value]) -> Result<String> {
727 if let Some(arg) = args.first() {
728 Ok(format!("Updated: {}", arg.to_string()))
729 } else {
730 Ok("Updated".to_string())
731 }
732 }
733
734 fn handle_timestamp_function(&self) -> Result<String> {
736 use std::time::{SystemTime, UNIX_EPOCH};
737 let timestamp = SystemTime::now()
738 .duration_since(UNIX_EPOCH)
739 .map_err(|e| RuleEngineError::EvaluationError {
740 message: format!("Failed to get timestamp: {}", e),
741 })?
742 .as_secs();
743 Ok(timestamp.to_string())
744 }
745
746 fn handle_random_function(&self, args: &[Value]) -> Result<String> {
748 use std::collections::hash_map::DefaultHasher;
749 use std::hash::{Hash, Hasher};
750
751 let mut hasher = DefaultHasher::new();
753 std::time::SystemTime::now().hash(&mut hasher);
754 let random_value = hasher.finish();
755
756 if args.is_empty() {
757 Ok((random_value % 100).to_string()) } else if let Some(Value::Number(max)) = args.first() {
759 let max_val = *max as u64;
760 Ok((random_value % max_val).to_string())
761 } else {
762 Ok(random_value.to_string())
763 }
764 }
765
766 fn handle_format_function(&self, args: &[Value]) -> Result<String> {
768 if args.is_empty() {
769 return Ok("".to_string());
770 }
771
772 let template = args[0].to_string();
773 let values: Vec<String> = args[1..].iter().map(|v| v.to_string()).collect();
774
775 let mut result = template;
777 for (i, value) in values.iter().enumerate() {
778 result = result.replace(&format!("{{{}}}", i), value);
779 }
780
781 Ok(result)
782 }
783
784 fn handle_length_function(&self, args: &[Value]) -> Result<String> {
786 if let Some(arg) = args.first() {
787 match arg {
788 Value::String(s) => Ok(s.len().to_string()),
789 Value::Array(arr) => Ok(arr.len().to_string()),
790 Value::Object(obj) => Ok(obj.len().to_string()),
791 _ => Ok("1".to_string()), }
793 } else {
794 Ok("0".to_string())
795 }
796 }
797
798 fn handle_sum_function(&self, args: &[Value]) -> Result<String> {
800 let sum = args.iter().fold(0.0, |acc, val| match val {
801 Value::Number(n) => acc + n,
802 Value::Integer(i) => acc + (*i as f64),
803 _ => acc,
804 });
805 Ok(sum.to_string())
806 }
807
808 fn handle_max_function(&self, args: &[Value]) -> Result<String> {
810 let max = args.iter().fold(f64::NEG_INFINITY, |acc, val| match val {
811 Value::Number(n) => acc.max(*n),
812 Value::Integer(i) => acc.max(*i as f64),
813 _ => acc,
814 });
815 Ok(max.to_string())
816 }
817
818 fn handle_min_function(&self, args: &[Value]) -> Result<String> {
820 let min = args.iter().fold(f64::INFINITY, |acc, val| match val {
821 Value::Number(n) => acc.min(*n),
822 Value::Integer(i) => acc.min(*i as f64),
823 _ => acc,
824 });
825 Ok(min.to_string())
826 }
827
828 fn handle_average_function(&self, args: &[Value]) -> Result<String> {
830 if args.is_empty() {
831 return Ok("0".to_string());
832 }
833
834 let (sum, count) = args.iter().fold((0.0, 0), |(sum, count), val| match val {
835 Value::Number(n) => (sum + n, count + 1),
836 Value::Integer(i) => (sum + (*i as f64), count + 1),
837 _ => (sum, count),
838 });
839
840 if count > 0 {
841 Ok((sum / count as f64).to_string())
842 } else {
843 Ok("0".to_string())
844 }
845 }
846
847 fn handle_round_function(&self, args: &[Value]) -> Result<String> {
849 if let Some(Value::Number(n)) = args.first() {
850 Ok(n.round().to_string())
851 } else if let Some(Value::Integer(i)) = args.first() {
852 Ok(i.to_string())
853 } else {
854 Err(RuleEngineError::EvaluationError {
855 message: "round() requires a numeric argument".to_string(),
856 })
857 }
858 }
859
860 fn handle_floor_function(&self, args: &[Value]) -> Result<String> {
861 if let Some(Value::Number(n)) = args.first() {
862 Ok(n.floor().to_string())
863 } else if let Some(Value::Integer(i)) = args.first() {
864 Ok(i.to_string())
865 } else {
866 Err(RuleEngineError::EvaluationError {
867 message: "floor() requires a numeric argument".to_string(),
868 })
869 }
870 }
871
872 fn handle_ceil_function(&self, args: &[Value]) -> Result<String> {
873 if let Some(Value::Number(n)) = args.first() {
874 Ok(n.ceil().to_string())
875 } else if let Some(Value::Integer(i)) = args.first() {
876 Ok(i.to_string())
877 } else {
878 Err(RuleEngineError::EvaluationError {
879 message: "ceil() requires a numeric argument".to_string(),
880 })
881 }
882 }
883
884 fn handle_abs_function(&self, args: &[Value]) -> Result<String> {
885 if let Some(Value::Number(n)) = args.first() {
886 Ok(n.abs().to_string())
887 } else if let Some(Value::Integer(i)) = args.first() {
888 Ok(i.abs().to_string())
889 } else {
890 Err(RuleEngineError::EvaluationError {
891 message: "abs() requires a numeric argument".to_string(),
892 })
893 }
894 }
895
896 fn handle_contains_function(&self, args: &[Value]) -> Result<String> {
898 if args.len() >= 2 {
899 let haystack = args[0].to_string();
900 let needle = args[1].to_string();
901 Ok(haystack.contains(&needle).to_string())
902 } else {
903 Err(RuleEngineError::EvaluationError {
904 message: "contains() requires 2 arguments".to_string(),
905 })
906 }
907 }
908
909 fn handle_starts_with_function(&self, args: &[Value]) -> Result<String> {
910 if args.len() >= 2 {
911 let text = args[0].to_string();
912 let prefix = args[1].to_string();
913 Ok(text.starts_with(&prefix).to_string())
914 } else {
915 Err(RuleEngineError::EvaluationError {
916 message: "startswith() requires 2 arguments".to_string(),
917 })
918 }
919 }
920
921 fn handle_ends_with_function(&self, args: &[Value]) -> Result<String> {
922 if args.len() >= 2 {
923 let text = args[0].to_string();
924 let suffix = args[1].to_string();
925 Ok(text.ends_with(&suffix).to_string())
926 } else {
927 Err(RuleEngineError::EvaluationError {
928 message: "endswith() requires 2 arguments".to_string(),
929 })
930 }
931 }
932
933 fn handle_lowercase_function(&self, args: &[Value]) -> Result<String> {
934 if let Some(arg) = args.first() {
935 Ok(arg.to_string().to_lowercase())
936 } else {
937 Err(RuleEngineError::EvaluationError {
938 message: "lowercase() requires 1 argument".to_string(),
939 })
940 }
941 }
942
943 fn handle_uppercase_function(&self, args: &[Value]) -> Result<String> {
944 if let Some(arg) = args.first() {
945 Ok(arg.to_string().to_uppercase())
946 } else {
947 Err(RuleEngineError::EvaluationError {
948 message: "uppercase() requires 1 argument".to_string(),
949 })
950 }
951 }
952
953 fn handle_trim_function(&self, args: &[Value]) -> Result<String> {
954 if let Some(arg) = args.first() {
955 Ok(arg.to_string().trim().to_string())
956 } else {
957 Err(RuleEngineError::EvaluationError {
958 message: "trim() requires 1 argument".to_string(),
959 })
960 }
961 }
962
963 fn handle_split_function(&self, args: &[Value]) -> Result<String> {
964 if args.len() >= 2 {
965 let text = args[0].to_string();
966 let delimiter = args[1].to_string();
967 let parts: Vec<String> = text.split(&delimiter).map(|s| s.to_string()).collect();
968 Ok(format!("{:?}", parts)) } else {
970 Err(RuleEngineError::EvaluationError {
971 message: "split() requires 2 arguments".to_string(),
972 })
973 }
974 }
975
976 fn handle_join_function(&self, args: &[Value]) -> Result<String> {
977 if args.len() >= 2 {
978 let delimiter = args[0].to_string();
979 let parts: Vec<String> = args[1..].iter().map(|v| v.to_string()).collect();
980 Ok(parts.join(&delimiter))
981 } else {
982 Err(RuleEngineError::EvaluationError {
983 message: "join() requires at least 2 arguments".to_string(),
984 })
985 }
986 }
987
988 fn handle_custom_function(
990 &self,
991 function: &str,
992 args: &[Value],
993 facts: &Facts,
994 ) -> Result<String> {
995 if let Some(custom_func) = self.custom_functions.get(function) {
997 if self.config.debug_mode {
998 println!("π― Calling registered function: {}({:?})", function, args);
999 }
1000
1001 match custom_func(args, facts) {
1002 Ok(result) => Ok(result.to_string()),
1003 Err(e) => Err(e),
1004 }
1005 } else {
1006 if self.config.debug_mode {
1008 println!("β οΈ Custom function '{}' not registered", function);
1009 }
1010
1011 Err(RuleEngineError::EvaluationError {
1012 message: format!("Function '{}' is not registered. Use engine.register_function() to add custom functions.", function),
1013 })
1014 }
1015 }
1016
1017 fn execute_method_call(
1019 &self,
1020 object_name: &str,
1021 method: &str,
1022 args: &[Value],
1023 facts: &Facts,
1024 ) -> Result<String> {
1025 let Some(object_value) = facts.get(object_name) else {
1027 return Err(RuleEngineError::EvaluationError {
1028 message: format!("Object '{}' not found in facts", object_name),
1029 });
1030 };
1031
1032 let method_lower = method.to_lowercase();
1033
1034 if method_lower.starts_with("set") && args.len() == 1 {
1036 return self.handle_setter_method(object_name, method, &args[0], object_value, facts);
1037 }
1038
1039 if method_lower.starts_with("get") && args.is_empty() {
1041 return self.handle_getter_method(object_name, method, &object_value);
1042 }
1043
1044 match method_lower.as_str() {
1046 "tostring" => Ok(object_value.to_string()),
1047 "update" => {
1048 facts.add_value(object_name, object_value)?;
1049 Ok(format!("Updated {}", object_name))
1050 }
1051 "reset" => self.handle_reset_method(object_name, object_value, facts),
1052 _ => self.handle_property_access_or_fallback(
1053 object_name,
1054 method,
1055 args.len(),
1056 &object_value,
1057 ),
1058 }
1059 }
1060
1061 fn handle_setter_method(
1063 &self,
1064 object_name: &str,
1065 method: &str,
1066 new_value: &Value,
1067 mut object_value: Value,
1068 facts: &Facts,
1069 ) -> Result<String> {
1070 let property_name = Self::extract_property_name_from_setter(method);
1071
1072 match object_value {
1073 Value::Object(ref mut obj) => {
1074 obj.insert(property_name.clone(), new_value.clone());
1075 facts.add_value(object_name, object_value)?;
1076 Ok(format!(
1077 "Set {} to {}",
1078 property_name,
1079 new_value.to_string()
1080 ))
1081 }
1082 _ => Err(RuleEngineError::EvaluationError {
1083 message: format!("Cannot call setter on non-object type: {}", object_name),
1084 }),
1085 }
1086 }
1087
1088 fn handle_getter_method(
1090 &self,
1091 object_name: &str,
1092 method: &str,
1093 object_value: &Value,
1094 ) -> Result<String> {
1095 let property_name = Self::extract_property_name_from_getter(method);
1096
1097 match object_value {
1098 Value::Object(obj) => {
1099 if let Some(value) = obj.get(&property_name) {
1100 Ok(value.to_string())
1101 } else {
1102 Err(RuleEngineError::EvaluationError {
1103 message: format!(
1104 "Property '{}' not found on object '{}'",
1105 property_name, object_name
1106 ),
1107 })
1108 }
1109 }
1110 _ => Err(RuleEngineError::EvaluationError {
1111 message: format!("Cannot call getter on non-object type: {}", object_name),
1112 }),
1113 }
1114 }
1115
1116 fn handle_reset_method(
1118 &self,
1119 object_name: &str,
1120 mut object_value: Value,
1121 facts: &Facts,
1122 ) -> Result<String> {
1123 match object_value {
1124 Value::Object(ref mut obj) => {
1125 obj.clear();
1126 facts.add_value(object_name, object_value)?;
1127 Ok(format!("Reset {}", object_name))
1128 }
1129 _ => Err(RuleEngineError::EvaluationError {
1130 message: format!("Cannot reset non-object type: {}", object_name),
1131 }),
1132 }
1133 }
1134
1135 fn handle_property_access_or_fallback(
1137 &self,
1138 object_name: &str,
1139 method: &str,
1140 arg_count: usize,
1141 object_value: &Value,
1142 ) -> Result<String> {
1143 if let Value::Object(obj) = object_value {
1144 if let Some(value) = obj.get(method) {
1146 return Ok(value.to_string());
1147 }
1148
1149 let capitalized_method = Self::capitalize_first_letter(method);
1151 if let Some(value) = obj.get(&capitalized_method) {
1152 return Ok(value.to_string());
1153 }
1154 }
1155
1156 Ok(format!(
1158 "Called {}.{} with {} args",
1159 object_name, method, arg_count
1160 ))
1161 }
1162
1163 fn extract_property_name_from_setter(method: &str) -> String {
1165 let property_name = &method[3..]; Self::capitalize_first_letter(property_name)
1167 }
1168
1169 fn extract_property_name_from_getter(method: &str) -> String {
1171 let property_name = &method[3..]; Self::capitalize_first_letter(property_name)
1173 }
1174
1175 fn capitalize_first_letter(s: &str) -> String {
1177 if s.is_empty() {
1178 return String::new();
1179 }
1180 let mut chars = s.chars();
1181 match chars.next() {
1182 None => String::new(),
1183 Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
1184 }
1185 }
1186
1187 fn resolve_action_parameters(
1189 &self,
1190 params: &HashMap<String, Value>,
1191 facts: &Facts,
1192 ) -> Result<HashMap<String, Value>> {
1193 let mut resolved = HashMap::new();
1194
1195 for (key, value) in params {
1196 let resolved_value = match value {
1197 Value::String(s) => {
1198 if s.contains('.') {
1200 if let Some(fact_value) = facts.get_nested(s) {
1202 fact_value
1203 } else {
1204 value.clone()
1206 }
1207 } else {
1208 value.clone()
1209 }
1210 }
1211 _ => value.clone(),
1212 };
1213 resolved.insert(key.clone(), resolved_value);
1214 }
1215
1216 Ok(resolved)
1217 }
1218}