1use crate::engine::rule::{Condition, ConditionGroup, Rule};
115use crate::engine::condition_evaluator::ConditionEvaluator;
116use crate::types::{ActionType, Value};
117use crate::{Facts, KnowledgeBase};
118use crate::errors::{Result, RuleEngineError};
119
120pub struct RuleExecutor {
122 evaluator: ConditionEvaluator,
123 tms_inserter: Option<std::sync::Arc<dyn Fn(String, crate::rete::TypedFacts, String, Vec<String>) + Send + Sync>>,
127}
128
129impl RuleExecutor {
130 pub fn new(_knowledge_base: KnowledgeBase) -> Self {
135 Self::new_with_inserter(_knowledge_base, None)
136 }
137
138 pub fn new_with_inserter(
143 _knowledge_base: KnowledgeBase,
144 inserter: Option<std::sync::Arc<dyn Fn(String, crate::rete::TypedFacts, String, Vec<String>) + Send + Sync>>,
145 ) -> Self {
146 Self {
147 evaluator: ConditionEvaluator::with_builtin_functions(),
148 tms_inserter: inserter,
149 }
150 }
151
152 pub fn try_execute_rule(
159 &self,
160 rule: &Rule,
161 facts: &mut Facts,
162 ) -> Result<bool> {
163 if !self.evaluate_conditions(&rule.conditions, facts)? {
165 return Ok(false);
166 }
167
168 self.execute_actions(rule, facts)?;
170
171 Ok(true)
172 }
173
174 pub fn evaluate_conditions(
176 &self,
177 group: &ConditionGroup,
178 facts: &Facts,
179 ) -> Result<bool> {
180 self.evaluator.evaluate_conditions(group, facts)
182 }
183
184 pub fn evaluate_condition(&self, condition: &Condition, facts: &Facts) -> Result<bool> {
186 self.evaluator.evaluate_condition(condition, facts)
188 }
189
190 fn execute_actions(&self, rule: &Rule, facts: &mut Facts) -> Result<()> {
192 for action in &rule.actions {
193 self.execute_action(Some(rule), action, facts)?;
194 }
195
196 Ok(())
197 }
198
199 fn execute_action(&self, rule: Option<&Rule>, action: &ActionType, facts: &mut Facts) -> Result<()> {
201 match action {
202 ActionType::Set { field, value } => {
203 let evaluated_value = self.evaluate_value_expression(value, facts)?;
205
206 if let Some(inserter) = &self.tms_inserter {
209 if let Some(dot_pos) = field.find('.') {
210 let fact_type = field[..dot_pos].to_string();
211 let field_name = field[dot_pos + 1..].to_string();
212
213 let mut typed = crate::rete::TypedFacts::new();
215 let fv = match &evaluated_value {
217 crate::types::Value::String(s) => crate::rete::FactValue::String(s.clone()),
218 crate::types::Value::Integer(i) => crate::rete::FactValue::Integer(*i),
219 crate::types::Value::Number(n) => crate::rete::FactValue::Float(*n),
220 crate::types::Value::Boolean(b) => crate::rete::FactValue::Boolean(*b),
221 _ => crate::rete::FactValue::String(format!("{:?}", evaluated_value)),
222 };
223
224 typed.set(field_name, fv);
225
226 let premises = match rule {
229 Some(r) => self.collect_premise_keys_from_rule(r, facts),
230 None => Vec::new(),
231 };
232
233 let source_name = rule.map(|r| r.name.clone()).unwrap_or_else(|| "<unknown>".to_string());
235 (inserter)(fact_type, typed, source_name, premises);
236 facts.set(field, evaluated_value);
238 return Ok(());
239 }
240 }
241
242 facts.set(field, evaluated_value);
244 Ok(())
245 }
246
247 ActionType::MethodCall { object, method, args } => {
248 if let Some(obj_value) = facts.get(object) {
250 let mut obj_value = obj_value.clone();
251 let mut arg_values = Vec::new();
253 for arg in args {
254 let val = self.evaluate_value_expression(arg, facts)?;
255 arg_values.push(val);
256 }
257
258 let result = obj_value.call_method(method, arg_values)
260 .map_err(|e| RuleEngineError::ExecutionError(e))?;
261
262 facts.set(object, obj_value);
264
265 if result != Value::Null {
267 facts.set(&format!("{}._return", object), result);
268 }
269
270 Ok(())
271 } else {
272 Err(RuleEngineError::ExecutionError(
273 format!("Object not found: {}", object)
274 ))
275 }
276 }
277
278 ActionType::Retract { object } => {
279 facts.remove(object);
282 Ok(())
283 }
284
285 ActionType::Log { message } => {
286 println!("[BC Action] {}", message);
288 Ok(())
289 }
290
291 ActionType::Custom { .. } => {
292 Ok(())
294 }
295
296 ActionType::ActivateAgendaGroup { .. } => {
297 Ok(())
299 }
300
301 ActionType::ScheduleRule { .. } => {
302 Ok(())
304 }
305
306 ActionType::CompleteWorkflow { .. } => {
307 Ok(())
309 }
310
311 ActionType::SetWorkflowData { .. } => {
312 Ok(())
314 }
315 }
316 }
317
318 fn collect_premise_keys_from_rule(&self, rule: &Rule, facts: &Facts) -> Vec<String> {
323 use crate::engine::rule::{ConditionGroup, ConditionExpression};
324
325 let mut keys = Vec::new();
326
327 fn collect_from_group(group: &ConditionGroup, keys: &mut Vec<String>, facts: &Facts) {
328 match group {
329 ConditionGroup::Single(cond) => {
330 if let ConditionExpression::Field(f) = &cond.expression {
331 if let Some(dot_pos) = f.find('.') {
332 let fact_type = &f[..dot_pos];
333 let field_name = &f[dot_pos + 1..];
334
335 if let Some(val) = facts.get(f).or_else(|| facts.get_nested(f)) {
337 let value_str = match val {
338 crate::types::Value::String(s) => s.clone(),
339 crate::types::Value::Integer(i) => i.to_string(),
340 crate::types::Value::Number(n) => n.to_string(),
341 crate::types::Value::Boolean(b) => b.to_string(),
342 _ => format!("{:?}", val),
343 };
344
345 keys.push(format!("{}.{}={}", fact_type, field_name, value_str));
346 } else {
347 keys.push(format!("{}.{}=", fact_type, field_name));
349 }
350 }
351 }
352 }
353 ConditionGroup::Compound { left, right, .. } => {
354 collect_from_group(left, keys, facts);
355 collect_from_group(right, keys, facts);
356 }
357 _ => {}
359 }
360 }
361
362 collect_from_group(&rule.conditions, &mut keys, facts);
363 keys
364 }
365
366 fn evaluate_value_expression(&self, value: &Value, facts: &Facts) -> Result<Value> {
368 match value {
369 Value::Expression(expr) => {
370 if let Some(val) = facts.get(expr).or_else(|| facts.get_nested(expr)) {
372 return Ok(val);
373 }
374
375 if let Some(result) = self.try_evaluate_arithmetic(expr, facts) {
377 return Ok(result);
378 }
379
380 if expr == "true" {
382 Ok(Value::Boolean(true))
383 } else if expr == "false" {
384 Ok(Value::Boolean(false))
385 } else if expr == "null" {
386 Ok(Value::Null)
387 } else if let Ok(n) = expr.parse::<f64>() {
388 Ok(Value::Number(n))
389 } else if let Ok(i) = expr.parse::<i64>() {
390 Ok(Value::Integer(i))
391 } else {
392 if expr == "true" {
394 Ok(Value::Boolean(true))
395 } else if expr == "false" {
396 Ok(Value::Boolean(false))
397 } else if expr == "null" {
398 Ok(Value::Null)
399 } else if let Ok(n) = expr.parse::<f64>() {
400 Ok(Value::Number(n))
401 } else if let Ok(i) = expr.parse::<i64>() {
402 Ok(Value::Integer(i))
403 } else {
404 Ok(value.clone())
405 }
406 }
407 }
408 _ => Ok(value.clone()),
409 }
410 }
411
412 fn try_evaluate_arithmetic(&self, expr: &str, facts: &Facts) -> Option<Value> {
415 if let Some(div_pos) = expr.find(" / ") {
417 let left = expr[..div_pos].trim();
418 let right = expr[div_pos + 3..].trim();
419
420 let left_val = self.get_numeric_value(left, facts)?;
421 let right_val = self.get_numeric_value(right, facts)?;
422
423 if right_val != 0.0 {
424 return Some(Value::Number(left_val / right_val));
425 }
426 return None;
427 }
428
429 if let Some(mul_pos) = expr.find(" * ") {
431 let left = expr[..mul_pos].trim();
432 let right = expr[mul_pos + 3..].trim();
433
434 let left_val = self.get_numeric_value(left, facts)?;
435 let right_val = self.get_numeric_value(right, facts)?;
436
437 return Some(Value::Number(left_val * right_val));
438 }
439
440 if let Some(add_pos) = expr.find(" + ") {
442 let left = expr[..add_pos].trim();
443 let right = expr[add_pos + 3..].trim();
444
445 let left_val = self.get_numeric_value(left, facts)?;
446 let right_val = self.get_numeric_value(right, facts)?;
447
448 return Some(Value::Number(left_val + right_val));
449 }
450
451 if let Some(sub_pos) = expr.find(" - ") {
453 let left = expr[..sub_pos].trim();
454 let right = expr[sub_pos + 3..].trim();
455
456 let left_val = self.get_numeric_value(left, facts)?;
457 let right_val = self.get_numeric_value(right, facts)?;
458
459 return Some(Value::Number(left_val - right_val));
460 }
461
462 None
463 }
464
465 fn get_numeric_value(&self, s: &str, facts: &Facts) -> Option<f64> {
467 if let Ok(n) = s.parse::<f64>() {
469 return Some(n);
470 }
471
472 if let Some(val) = facts.get(s).or_else(|| facts.get_nested(s)) {
474 match val {
475 Value::Number(n) => Some(n),
476 Value::Integer(i) => Some(i as f64),
477 _ => None,
478 }
479 } else {
480 None
481 }
482 }
483}
484
485#[cfg(test)]
486mod tests {
487 use super::*;
488 use crate::types::Operator;
489
490 #[test]
491 fn test_evaluate_simple_condition() {
492 let kb = KnowledgeBase::new("test");
493 let executor = RuleExecutor::new(kb);
494
495 let mut facts = Facts::new();
496 facts.set("User.Age", Value::Number(25.0));
497
498 let condition = Condition::new(
499 "User.Age".to_string(),
500 Operator::GreaterThan,
501 Value::Number(18.0),
502 );
503
504 let result = executor.evaluate_condition(&condition, &facts).unwrap();
505 assert!(result);
506 }
507
508 #[test]
509 fn test_evaluate_function_call_len() {
510 let kb = KnowledgeBase::new("test");
511 let executor = RuleExecutor::new(kb);
512
513 let mut facts = Facts::new();
514 facts.set("User.Name", Value::String("John".to_string()));
515
516 let condition = Condition::with_function(
517 "len".to_string(),
518 vec!["User.Name".to_string()],
519 Operator::GreaterThan,
520 Value::Number(3.0),
521 );
522
523 let result = executor.evaluate_condition(&condition, &facts).unwrap();
524 assert!(result); }
526
527 #[test]
528 fn test_execute_set_action() {
529 let kb = KnowledgeBase::new("test");
530 let executor = RuleExecutor::new(kb);
531
532 let mut facts = Facts::new();
533
534 let action = ActionType::Set {
535 field: "User.IsVIP".to_string(),
536 value: Value::Boolean(true),
537 };
538
539 executor.execute_action(None, &action, &mut facts).unwrap();
540
541 assert_eq!(facts.get("User.IsVIP"), Some(Value::Boolean(true)));
542 }
543
544 #[test]
545 fn test_evaluate_compound_and_condition() {
546 let kb = KnowledgeBase::new("test");
547 let executor = RuleExecutor::new(kb);
548
549 let mut facts = Facts::new();
550 facts.set("User.Age", Value::Number(25.0));
551 facts.set("User.Country", Value::String("US".to_string()));
552
553 let conditions = ConditionGroup::Compound {
554 left: Box::new(ConditionGroup::Single(Condition::new(
555 "User.Age".to_string(),
556 Operator::GreaterThan,
557 Value::Number(18.0),
558 ))),
559 operator: crate::types::LogicalOperator::And,
560 right: Box::new(ConditionGroup::Single(Condition::new(
561 "User.Country".to_string(),
562 Operator::Equal,
563 Value::String("US".to_string()),
564 ))),
565 };
566
567 let result = executor.evaluate_conditions(&conditions, &facts).unwrap();
568 assert!(result);
569 }
570
571 #[test]
572 fn test_evaluate_compound_or_condition() {
573 let kb = KnowledgeBase::new("test");
574 let executor = RuleExecutor::new(kb);
575
576 let mut facts = Facts::new();
577 facts.set("User.Age", Value::Number(15.0));
578 facts.set("User.HasParentalConsent", Value::Boolean(true));
579
580 let conditions = ConditionGroup::Compound {
581 left: Box::new(ConditionGroup::Single(Condition::new(
582 "User.Age".to_string(),
583 Operator::GreaterThan,
584 Value::Number(18.0),
585 ))),
586 operator: crate::types::LogicalOperator::Or,
587 right: Box::new(ConditionGroup::Single(Condition::new(
588 "User.HasParentalConsent".to_string(),
589 Operator::Equal,
590 Value::Boolean(true),
591 ))),
592 };
593
594 let result = executor.evaluate_conditions(&conditions, &facts).unwrap();
595 assert!(result); }
597
598 #[test]
599 fn test_evaluate_not_condition() {
600 let kb = KnowledgeBase::new("test");
601 let executor = RuleExecutor::new(kb);
602
603 let mut facts = Facts::new();
604 facts.set("User.IsBanned", Value::Boolean(false));
605
606 let conditions = ConditionGroup::Not(Box::new(ConditionGroup::Single(
607 Condition::new(
608 "User.IsBanned".to_string(),
609 Operator::Equal,
610 Value::Boolean(true),
611 )
612 )));
613
614 let result = executor.evaluate_conditions(&conditions, &facts).unwrap();
615 assert!(result); }
617
618 #[test]
619 fn test_evaluate_function_isempty() {
620 let kb = KnowledgeBase::new("test");
621 let executor = RuleExecutor::new(kb);
622
623 let mut facts = Facts::new();
624 facts.set("User.Description", Value::String("".to_string()));
625
626 let condition = Condition::with_function(
627 "isEmpty".to_string(),
628 vec!["User.Description".to_string()],
629 Operator::Equal,
630 Value::Boolean(true),
631 );
632
633 let result = executor.evaluate_condition(&condition, &facts).unwrap();
634 assert!(result); }
636
637 #[test]
638 fn test_evaluate_test_expression_exists() {
639 let kb = KnowledgeBase::new("test");
640 let executor = RuleExecutor::new(kb);
641
642 let mut facts = Facts::new();
643 facts.set("User.Email", Value::String("user@example.com".to_string()));
644
645 let condition = Condition {
646 field: "User.Email".to_string(),
647 expression: crate::engine::rule::ConditionExpression::Test {
648 name: "exists".to_string(),
649 args: vec!["User.Email".to_string()],
650 },
651 operator: Operator::Equal,
652 value: Value::Boolean(true),
653 };
654
655 let result = executor.evaluate_condition(&condition, &facts).unwrap();
656 assert!(result);
657 }
658
659 #[test]
660 fn test_execute_log_action() {
661 let kb = KnowledgeBase::new("test");
662 let executor = RuleExecutor::new(kb);
663
664 let mut facts = Facts::new();
665
666 let action = ActionType::Log {
667 message: "Test log message".to_string(),
668 };
669
670 executor.execute_action(None, &action, &mut facts).unwrap();
672 }
673
674 #[test]
675 fn test_try_execute_rule_success() {
676 let kb = KnowledgeBase::new("test");
677 let executor = RuleExecutor::new(kb);
678
679 let mut facts = Facts::new();
680 facts.set("User.Age", Value::Number(25.0));
681
682 let conditions = ConditionGroup::Single(Condition::new(
683 "User.Age".to_string(),
684 Operator::GreaterThan,
685 Value::Number(18.0),
686 ));
687
688 let actions = vec![ActionType::Set {
689 field: "User.IsAdult".to_string(),
690 value: Value::Boolean(true),
691 }];
692
693 let rule = Rule::new("CheckAdult".to_string(), conditions, actions);
694
695 let executed = executor.try_execute_rule(&rule, &mut facts).unwrap();
696 assert!(executed);
697 assert_eq!(facts.get("User.IsAdult"), Some(Value::Boolean(true)));
698 }
699
700 #[test]
701 fn test_try_execute_rule_failure() {
702 let kb = KnowledgeBase::new("test");
703 let executor = RuleExecutor::new(kb);
704
705 let mut facts = Facts::new();
706 facts.set("User.Age", Value::Number(15.0));
707
708 let conditions = ConditionGroup::Single(Condition::new(
709 "User.Age".to_string(),
710 Operator::GreaterThan,
711 Value::Number(18.0),
712 ));
713
714 let actions = vec![ActionType::Set {
715 field: "User.IsAdult".to_string(),
716 value: Value::Boolean(true),
717 }];
718
719 let rule = Rule::new("CheckAdult".to_string(), conditions, actions);
720
721 let executed = executor.try_execute_rule(&rule, &mut facts).unwrap();
722 assert!(!executed); assert_eq!(facts.get("User.IsAdult"), None); }
725
726 #[test]
727 fn test_evaluate_string_operators() {
728 let kb = KnowledgeBase::new("test");
729 let executor = RuleExecutor::new(kb);
730
731 let mut facts = Facts::new();
732 facts.set("User.Email", Value::String("user@example.com".to_string()));
733
734 let condition = Condition::new(
736 "User.Email".to_string(),
737 Operator::Contains,
738 Value::String("@example".to_string()),
739 );
740 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
741
742 let condition = Condition::new(
744 "User.Email".to_string(),
745 Operator::StartsWith,
746 Value::String("user".to_string()),
747 );
748 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
749
750 let condition = Condition::new(
752 "User.Email".to_string(),
753 Operator::EndsWith,
754 Value::String(".com".to_string()),
755 );
756 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
757 }
758
759 #[test]
760 fn test_evaluate_numeric_operators() {
761 let kb = KnowledgeBase::new("test");
762 let executor = RuleExecutor::new(kb);
763
764 let mut facts = Facts::new();
765 facts.set("Order.Amount", Value::Number(1500.0));
766
767 let condition = Condition::new(
769 "Order.Amount".to_string(),
770 Operator::GreaterThanOrEqual,
771 Value::Number(1500.0),
772 );
773 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
774
775 let condition = Condition::new(
777 "Order.Amount".to_string(),
778 Operator::LessThan,
779 Value::Number(2000.0),
780 );
781 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
782
783 let condition = Condition::new(
785 "Order.Amount".to_string(),
786 Operator::NotEqual,
787 Value::Number(1000.0),
788 );
789 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
790 }
791
792 #[test]
793 fn test_evaluate_missing_field() {
794 let kb = KnowledgeBase::new("test");
795 let executor = RuleExecutor::new(kb);
796
797 let facts = Facts::new(); let condition = Condition::new(
800 "User.Age".to_string(),
801 Operator::GreaterThan,
802 Value::Number(18.0),
803 );
804
805 let result = executor.evaluate_condition(&condition, &facts).unwrap();
806 assert!(!result); }
808
809 #[test]
810 fn test_execute_multiple_actions() {
811 let kb = KnowledgeBase::new("test");
812 let executor = RuleExecutor::new(kb);
813
814 let mut facts = Facts::new();
815 facts.set("User.Points", Value::Number(150.0));
816
817 let conditions = ConditionGroup::Single(Condition::new(
818 "User.Points".to_string(),
819 Operator::GreaterThan,
820 Value::Number(100.0),
821 ));
822
823 let actions = vec![
824 ActionType::Set {
825 field: "User.IsVIP".to_string(),
826 value: Value::Boolean(true),
827 },
828 ActionType::Log {
829 message: "User promoted to VIP".to_string(),
830 },
831 ActionType::Set {
832 field: "User.Discount".to_string(),
833 value: Value::Number(0.2),
834 },
835 ];
836
837 let rule = Rule::new("PromoteToVIP".to_string(), conditions, actions);
838
839 let executed = executor.try_execute_rule(&rule, &mut facts).unwrap();
840 assert!(executed);
841 assert_eq!(facts.get("User.IsVIP"), Some(Value::Boolean(true)));
842 assert_eq!(facts.get("User.Discount"), Some(Value::Number(0.2)));
843 }
844
845 #[test]
846 fn test_evaluate_endswith_operator() {
847 let kb = KnowledgeBase::new("test");
848 let executor = RuleExecutor::new(kb);
849
850 let mut facts = Facts::new();
851 facts.set("User.Email", Value::String("user@example.com".to_string()));
852 facts.set("File.Name", Value::String("document.pdf".to_string()));
853 facts.set("Domain.URL", Value::String("https://api.example.org".to_string()));
854
855 let condition = Condition::new(
857 "User.Email".to_string(),
858 Operator::EndsWith,
859 Value::String(".com".to_string()),
860 );
861 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
862
863 let condition = Condition::new(
865 "File.Name".to_string(),
866 Operator::EndsWith,
867 Value::String(".pdf".to_string()),
868 );
869 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
870
871 let condition = Condition::new(
873 "Domain.URL".to_string(),
874 Operator::EndsWith,
875 Value::String(".org".to_string()),
876 );
877 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
878
879 let condition = Condition::new(
881 "User.Email".to_string(),
882 Operator::EndsWith,
883 Value::String(".net".to_string()),
884 );
885 assert!(!executor.evaluate_condition(&condition, &facts).unwrap());
886
887 let condition = Condition::new(
889 "File.Name".to_string(),
890 Operator::EndsWith,
891 Value::String("document.pdf".to_string()),
892 );
893 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
894 }
895
896 #[test]
897 fn test_evaluate_endswith_edge_cases() {
898 let kb = KnowledgeBase::new("test");
899 let executor = RuleExecutor::new(kb);
900
901 let mut facts = Facts::new();
902 facts.set("Empty.String", Value::String("".to_string()));
903 facts.set("Single.Char", Value::String("a".to_string()));
904 facts.set("Number.Value", Value::Number(123.0));
905
906 let condition = Condition::new(
908 "Empty.String".to_string(),
909 Operator::EndsWith,
910 Value::String("".to_string()),
911 );
912 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
913
914 let condition = Condition::new(
916 "Single.Char".to_string(),
917 Operator::EndsWith,
918 Value::String("a".to_string()),
919 );
920 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
921
922 let condition = Condition::new(
924 "Number.Value".to_string(),
925 Operator::EndsWith,
926 Value::String(".0".to_string()),
927 );
928 assert!(!executor.evaluate_condition(&condition, &facts).unwrap());
929
930 let condition = Condition::new(
932 "Missing.Field".to_string(),
933 Operator::EndsWith,
934 Value::String("test".to_string()),
935 );
936 assert!(!executor.evaluate_condition(&condition, &facts).unwrap());
937
938 let mut facts2 = Facts::new();
940 facts2.set("Text.Value", Value::String("HelloWorld".to_string()));
941
942 let condition = Condition::new(
943 "Text.Value".to_string(),
944 Operator::EndsWith,
945 Value::String("world".to_string()),
946 );
947 assert!(!executor.evaluate_condition(&condition, &facts2).unwrap()); let condition = Condition::new(
950 "Text.Value".to_string(),
951 Operator::EndsWith,
952 Value::String("World".to_string()),
953 );
954 assert!(executor.evaluate_condition(&condition, &facts2).unwrap()); }
956
957 #[test]
958 fn test_evaluate_matches_operator() {
959 let kb = KnowledgeBase::new("test");
960 let executor = RuleExecutor::new(kb);
961
962 let mut facts = Facts::new();
963 facts.set("User.Email", Value::String("user@example.com".to_string()));
964 facts.set("Product.Name", Value::String("Premium Laptop Model X".to_string()));
965 facts.set("Log.Message", Value::String("Error: Connection timeout".to_string()));
966
967 let condition = Condition::new(
969 "User.Email".to_string(),
970 Operator::Matches,
971 Value::String("example".to_string()),
972 );
973 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
974
975 let condition = Condition::new(
977 "Product.Name".to_string(),
978 Operator::Matches,
979 Value::String("Premium".to_string()),
980 );
981 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
982
983 let condition = Condition::new(
985 "Log.Message".to_string(),
986 Operator::Matches,
987 Value::String("Error".to_string()),
988 );
989 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
990
991 let condition = Condition::new(
993 "User.Email".to_string(),
994 Operator::Matches,
995 Value::String("notfound".to_string()),
996 );
997 assert!(!executor.evaluate_condition(&condition, &facts).unwrap());
998
999 let condition = Condition::new(
1001 "Product.Name".to_string(),
1002 Operator::Matches,
1003 Value::String("Laptop".to_string()),
1004 );
1005 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
1006
1007 let condition = Condition::new(
1009 "Log.Message".to_string(),
1010 Operator::Matches,
1011 Value::String("Error: Connection timeout".to_string()),
1012 );
1013 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
1014 }
1015
1016 #[test]
1017 fn test_evaluate_matches_edge_cases() {
1018 let kb = KnowledgeBase::new("test");
1019 let executor = RuleExecutor::new(kb);
1020
1021 let mut facts = Facts::new();
1022 facts.set("Empty.String", Value::String("".to_string()));
1023 facts.set("Single.Char", Value::String("x".to_string()));
1024 facts.set("Number.Value", Value::Number(456.0));
1025 facts.set("Special.Chars", Value::String("test@#$%^&*()".to_string()));
1026
1027 let condition = Condition::new(
1029 "Empty.String".to_string(),
1030 Operator::Matches,
1031 Value::String("".to_string()),
1032 );
1033 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
1034
1035 let condition = Condition::new(
1037 "Single.Char".to_string(),
1038 Operator::Matches,
1039 Value::String("x".to_string()),
1040 );
1041 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
1042
1043 let condition = Condition::new(
1045 "Number.Value".to_string(),
1046 Operator::Matches,
1047 Value::String("456".to_string()),
1048 );
1049 assert!(!executor.evaluate_condition(&condition, &facts).unwrap());
1050
1051 let condition = Condition::new(
1053 "Missing.Field".to_string(),
1054 Operator::Matches,
1055 Value::String("pattern".to_string()),
1056 );
1057 assert!(!executor.evaluate_condition(&condition, &facts).unwrap());
1058
1059 let condition = Condition::new(
1061 "Special.Chars".to_string(),
1062 Operator::Matches,
1063 Value::String("@#$".to_string()),
1064 );
1065 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
1066
1067 let mut facts2 = Facts::new();
1069 facts2.set("Text.Value", Value::String("HelloWorld".to_string()));
1070
1071 let condition = Condition::new(
1072 "Text.Value".to_string(),
1073 Operator::Matches,
1074 Value::String("hello".to_string()),
1075 );
1076 assert!(!executor.evaluate_condition(&condition, &facts2).unwrap()); let condition = Condition::new(
1079 "Text.Value".to_string(),
1080 Operator::Matches,
1081 Value::String("Hello".to_string()),
1082 );
1083 assert!(executor.evaluate_condition(&condition, &facts2).unwrap()); }
1085
1086 #[test]
1087 fn test_endswith_matches_in_rules() {
1088 let kb = KnowledgeBase::new("test");
1090
1091 let condition1 = Condition::new(
1093 "User.Email".to_string(),
1094 Operator::EndsWith,
1095 Value::String(".edu".to_string()),
1096 );
1097 let actions1 = vec![ActionType::Set {
1098 field: "User.IsStudent".to_string(),
1099 value: Value::Boolean(true),
1100 }];
1101 let rule1 = Rule::new(
1102 "StudentEmailRule".to_string(),
1103 ConditionGroup::Single(condition1),
1104 actions1,
1105 );
1106
1107 let condition2 = Condition::new(
1109 "Product.Name".to_string(),
1110 Operator::Matches,
1111 Value::String("Premium".to_string()),
1112 );
1113 let actions2 = vec![ActionType::Set {
1114 field: "Product.IsPremium".to_string(),
1115 value: Value::Boolean(true),
1116 }];
1117 let rule2 = Rule::new(
1118 "PremiumProductRule".to_string(),
1119 ConditionGroup::Single(condition2),
1120 actions2,
1121 );
1122
1123 let _ = kb.add_rule(rule1.clone());
1124 let _ = kb.add_rule(rule2.clone());
1125
1126 let executor = RuleExecutor::new(kb);
1127
1128 let mut facts1 = Facts::new();
1130 facts1.set("User.Email", Value::String("student@university.edu".to_string()));
1131
1132 let executed = executor.try_execute_rule(&rule1, &mut facts1).unwrap();
1133 assert!(executed);
1134 assert_eq!(facts1.get("User.IsStudent"), Some(Value::Boolean(true)));
1135
1136 let mut facts2 = Facts::new();
1138 facts2.set("Product.Name", Value::String("Premium Laptop X1".to_string()));
1139
1140 let executed = executor.try_execute_rule(&rule2, &mut facts2).unwrap();
1141 assert!(executed);
1142 assert_eq!(facts2.get("Product.IsPremium"), Some(Value::Boolean(true)));
1143
1144 let mut facts3 = Facts::new();
1146 facts3.set("User.Email", Value::String("user@company.com".to_string()));
1147
1148 let executed = executor.try_execute_rule(&rule1, &mut facts3).unwrap();
1149 assert!(!executed); }
1151}