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