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 }
333
334 fn collect_premise_keys_from_rule(&self, rule: &Rule, facts: &Facts) -> Vec<String> {
339 use crate::engine::rule::{ConditionExpression, ConditionGroup};
340
341 let mut keys = Vec::new();
342
343 fn collect_from_group(group: &ConditionGroup, keys: &mut Vec<String>, facts: &Facts) {
344 match group {
345 ConditionGroup::Single(cond) => {
346 if let ConditionExpression::Field(f) = &cond.expression {
347 if let Some(dot_pos) = f.find('.') {
348 let fact_type = &f[..dot_pos];
349 let field_name = &f[dot_pos + 1..];
350
351 if let Some(val) = facts.get(f).or_else(|| facts.get_nested(f)) {
353 let value_str = match val {
354 crate::types::Value::String(s) => s.clone(),
355 crate::types::Value::Integer(i) => i.to_string(),
356 crate::types::Value::Number(n) => n.to_string(),
357 crate::types::Value::Boolean(b) => b.to_string(),
358 _ => format!("{:?}", val),
359 };
360
361 keys.push(format!("{}.{}={}", fact_type, field_name, value_str));
362 } else {
363 keys.push(format!("{}.{}=", fact_type, field_name));
365 }
366 }
367 }
368 }
369 ConditionGroup::Compound { left, right, .. } => {
370 collect_from_group(left, keys, facts);
371 collect_from_group(right, keys, facts);
372 }
373 _ => {}
375 }
376 }
377
378 collect_from_group(&rule.conditions, &mut keys, facts);
379 keys
380 }
381
382 fn evaluate_value_expression(&self, value: &Value, facts: &Facts) -> Result<Value> {
384 match value {
385 Value::Expression(expr) => {
386 if let Some(val) = facts.get(expr).or_else(|| facts.get_nested(expr)) {
388 return Ok(val);
389 }
390
391 if let Some(result) = self.try_evaluate_arithmetic(expr, facts) {
393 return Ok(result);
394 }
395
396 if expr == "true" {
398 Ok(Value::Boolean(true))
399 } else if expr == "false" {
400 Ok(Value::Boolean(false))
401 } else if expr == "null" {
402 Ok(Value::Null)
403 } else if let Ok(n) = expr.parse::<f64>() {
404 Ok(Value::Number(n))
405 } else if let Ok(i) = expr.parse::<i64>() {
406 Ok(Value::Integer(i))
407 } else {
408 if expr == "true" {
410 Ok(Value::Boolean(true))
411 } else if expr == "false" {
412 Ok(Value::Boolean(false))
413 } else if expr == "null" {
414 Ok(Value::Null)
415 } else if let Ok(n) = expr.parse::<f64>() {
416 Ok(Value::Number(n))
417 } else if let Ok(i) = expr.parse::<i64>() {
418 Ok(Value::Integer(i))
419 } else {
420 Ok(value.clone())
421 }
422 }
423 }
424 _ => Ok(value.clone()),
425 }
426 }
427
428 fn try_evaluate_arithmetic(&self, expr: &str, facts: &Facts) -> Option<Value> {
431 if let Some(div_pos) = expr.find(" / ") {
433 let left = expr[..div_pos].trim();
434 let right = expr[div_pos + 3..].trim();
435
436 let left_val = self.get_numeric_value(left, facts)?;
437 let right_val = self.get_numeric_value(right, facts)?;
438
439 if right_val != 0.0 {
440 return Some(Value::Number(left_val / right_val));
441 }
442 return None;
443 }
444
445 if let Some(mul_pos) = expr.find(" * ") {
447 let left = expr[..mul_pos].trim();
448 let right = expr[mul_pos + 3..].trim();
449
450 let left_val = self.get_numeric_value(left, facts)?;
451 let right_val = self.get_numeric_value(right, facts)?;
452
453 return Some(Value::Number(left_val * right_val));
454 }
455
456 if let Some(add_pos) = expr.find(" + ") {
458 let left = expr[..add_pos].trim();
459 let right = expr[add_pos + 3..].trim();
460
461 let left_val = self.get_numeric_value(left, facts)?;
462 let right_val = self.get_numeric_value(right, facts)?;
463
464 return Some(Value::Number(left_val + right_val));
465 }
466
467 if let Some(sub_pos) = expr.find(" - ") {
469 let left = expr[..sub_pos].trim();
470 let right = expr[sub_pos + 3..].trim();
471
472 let left_val = self.get_numeric_value(left, facts)?;
473 let right_val = self.get_numeric_value(right, facts)?;
474
475 return Some(Value::Number(left_val - right_val));
476 }
477
478 None
479 }
480
481 fn get_numeric_value(&self, s: &str, facts: &Facts) -> Option<f64> {
483 if let Ok(n) = s.parse::<f64>() {
485 return Some(n);
486 }
487
488 if let Some(val) = facts.get(s).or_else(|| facts.get_nested(s)) {
490 match val {
491 Value::Number(n) => Some(n),
492 Value::Integer(i) => Some(i as f64),
493 _ => None,
494 }
495 } else {
496 None
497 }
498 }
499}
500
501#[cfg(test)]
502mod tests {
503 use super::*;
504 use crate::types::Operator;
505
506 #[test]
507 fn test_evaluate_simple_condition() {
508 let kb = KnowledgeBase::new("test");
509 let executor = RuleExecutor::new(kb);
510
511 let facts = Facts::new();
512 facts.set("User.Age", Value::Number(25.0));
513
514 let condition = Condition::new(
515 "User.Age".to_string(),
516 Operator::GreaterThan,
517 Value::Number(18.0),
518 );
519
520 let result = executor.evaluate_condition(&condition, &facts).unwrap();
521 assert!(result);
522 }
523
524 #[test]
525 fn test_evaluate_function_call_len() {
526 let kb = KnowledgeBase::new("test");
527 let executor = RuleExecutor::new(kb);
528
529 let facts = Facts::new();
530 facts.set("User.Name", Value::String("John".to_string()));
531
532 let condition = Condition::with_function(
533 "len".to_string(),
534 vec!["User.Name".to_string()],
535 Operator::GreaterThan,
536 Value::Number(3.0),
537 );
538
539 let result = executor.evaluate_condition(&condition, &facts).unwrap();
540 assert!(result); }
542
543 #[test]
544 fn test_execute_set_action() {
545 let kb = KnowledgeBase::new("test");
546 let executor = RuleExecutor::new(kb);
547
548 let mut facts = Facts::new();
549
550 let action = ActionType::Set {
551 field: "User.IsVIP".to_string(),
552 value: Value::Boolean(true),
553 };
554
555 executor.execute_action(None, &action, &mut facts).unwrap();
556
557 assert_eq!(facts.get("User.IsVIP"), Some(Value::Boolean(true)));
558 }
559
560 #[test]
561 fn test_evaluate_compound_and_condition() {
562 let kb = KnowledgeBase::new("test");
563 let executor = RuleExecutor::new(kb);
564
565 let facts = Facts::new();
566 facts.set("User.Age", Value::Number(25.0));
567 facts.set("User.Country", Value::String("US".to_string()));
568
569 let conditions = ConditionGroup::Compound {
570 left: Box::new(ConditionGroup::Single(Condition::new(
571 "User.Age".to_string(),
572 Operator::GreaterThan,
573 Value::Number(18.0),
574 ))),
575 operator: crate::types::LogicalOperator::And,
576 right: Box::new(ConditionGroup::Single(Condition::new(
577 "User.Country".to_string(),
578 Operator::Equal,
579 Value::String("US".to_string()),
580 ))),
581 };
582
583 let result = executor.evaluate_conditions(&conditions, &facts).unwrap();
584 assert!(result);
585 }
586
587 #[test]
588 fn test_evaluate_compound_or_condition() {
589 let kb = KnowledgeBase::new("test");
590 let executor = RuleExecutor::new(kb);
591
592 let facts = Facts::new();
593 facts.set("User.Age", Value::Number(15.0));
594 facts.set("User.HasParentalConsent", Value::Boolean(true));
595
596 let conditions = ConditionGroup::Compound {
597 left: Box::new(ConditionGroup::Single(Condition::new(
598 "User.Age".to_string(),
599 Operator::GreaterThan,
600 Value::Number(18.0),
601 ))),
602 operator: crate::types::LogicalOperator::Or,
603 right: Box::new(ConditionGroup::Single(Condition::new(
604 "User.HasParentalConsent".to_string(),
605 Operator::Equal,
606 Value::Boolean(true),
607 ))),
608 };
609
610 let result = executor.evaluate_conditions(&conditions, &facts).unwrap();
611 assert!(result); }
613
614 #[test]
615 fn test_evaluate_not_condition() {
616 let kb = KnowledgeBase::new("test");
617 let executor = RuleExecutor::new(kb);
618
619 let facts = Facts::new();
620 facts.set("User.IsBanned", Value::Boolean(false));
621
622 let conditions = ConditionGroup::Not(Box::new(ConditionGroup::Single(Condition::new(
623 "User.IsBanned".to_string(),
624 Operator::Equal,
625 Value::Boolean(true),
626 ))));
627
628 let result = executor.evaluate_conditions(&conditions, &facts).unwrap();
629 assert!(result); }
631
632 #[test]
633 fn test_evaluate_function_isempty() {
634 let kb = KnowledgeBase::new("test");
635 let executor = RuleExecutor::new(kb);
636
637 let facts = Facts::new();
638 facts.set("User.Description", Value::String("".to_string()));
639
640 let condition = Condition::with_function(
641 "isEmpty".to_string(),
642 vec!["User.Description".to_string()],
643 Operator::Equal,
644 Value::Boolean(true),
645 );
646
647 let result = executor.evaluate_condition(&condition, &facts).unwrap();
648 assert!(result); }
650
651 #[test]
652 fn test_evaluate_test_expression_exists() {
653 let kb = KnowledgeBase::new("test");
654 let executor = RuleExecutor::new(kb);
655
656 let facts = Facts::new();
657 facts.set("User.Email", Value::String("user@example.com".to_string()));
658
659 let condition = Condition {
660 field: "User.Email".to_string(),
661 expression: crate::engine::rule::ConditionExpression::Test {
662 name: "exists".to_string(),
663 args: vec!["User.Email".to_string()],
664 },
665 operator: Operator::Equal,
666 value: Value::Boolean(true),
667 };
668
669 let result = executor.evaluate_condition(&condition, &facts).unwrap();
670 assert!(result);
671 }
672
673 #[test]
674 fn test_execute_log_action() {
675 let kb = KnowledgeBase::new("test");
676 let executor = RuleExecutor::new(kb);
677
678 let mut facts = Facts::new();
679
680 let action = ActionType::Log {
681 message: "Test log message".to_string(),
682 };
683
684 executor.execute_action(None, &action, &mut facts).unwrap();
686 }
687
688 #[test]
689 fn test_try_execute_rule_success() {
690 let kb = KnowledgeBase::new("test");
691 let executor = RuleExecutor::new(kb);
692
693 let mut facts = Facts::new();
694 facts.set("User.Age", Value::Number(25.0));
695
696 let conditions = ConditionGroup::Single(Condition::new(
697 "User.Age".to_string(),
698 Operator::GreaterThan,
699 Value::Number(18.0),
700 ));
701
702 let actions = vec![ActionType::Set {
703 field: "User.IsAdult".to_string(),
704 value: Value::Boolean(true),
705 }];
706
707 let rule = Rule::new("CheckAdult".to_string(), conditions, actions);
708
709 let executed = executor.try_execute_rule(&rule, &mut facts).unwrap();
710 assert!(executed);
711 assert_eq!(facts.get("User.IsAdult"), Some(Value::Boolean(true)));
712 }
713
714 #[test]
715 fn test_try_execute_rule_failure() {
716 let kb = KnowledgeBase::new("test");
717 let executor = RuleExecutor::new(kb);
718
719 let mut facts = Facts::new();
720 facts.set("User.Age", Value::Number(15.0));
721
722 let conditions = ConditionGroup::Single(Condition::new(
723 "User.Age".to_string(),
724 Operator::GreaterThan,
725 Value::Number(18.0),
726 ));
727
728 let actions = vec![ActionType::Set {
729 field: "User.IsAdult".to_string(),
730 value: Value::Boolean(true),
731 }];
732
733 let rule = Rule::new("CheckAdult".to_string(), conditions, actions);
734
735 let executed = executor.try_execute_rule(&rule, &mut facts).unwrap();
736 assert!(!executed); assert_eq!(facts.get("User.IsAdult"), None); }
739
740 #[test]
741 fn test_evaluate_string_operators() {
742 let kb = KnowledgeBase::new("test");
743 let executor = RuleExecutor::new(kb);
744
745 let facts = Facts::new();
746 facts.set("User.Email", Value::String("user@example.com".to_string()));
747
748 let condition = Condition::new(
750 "User.Email".to_string(),
751 Operator::Contains,
752 Value::String("@example".to_string()),
753 );
754 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
755
756 let condition = Condition::new(
758 "User.Email".to_string(),
759 Operator::StartsWith,
760 Value::String("user".to_string()),
761 );
762 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
763
764 let condition = Condition::new(
766 "User.Email".to_string(),
767 Operator::EndsWith,
768 Value::String(".com".to_string()),
769 );
770 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
771 }
772
773 #[test]
774 fn test_evaluate_numeric_operators() {
775 let kb = KnowledgeBase::new("test");
776 let executor = RuleExecutor::new(kb);
777
778 let facts = Facts::new();
779 facts.set("Order.Amount", Value::Number(1500.0));
780
781 let condition = Condition::new(
783 "Order.Amount".to_string(),
784 Operator::GreaterThanOrEqual,
785 Value::Number(1500.0),
786 );
787 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
788
789 let condition = Condition::new(
791 "Order.Amount".to_string(),
792 Operator::LessThan,
793 Value::Number(2000.0),
794 );
795 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
796
797 let condition = Condition::new(
799 "Order.Amount".to_string(),
800 Operator::NotEqual,
801 Value::Number(1000.0),
802 );
803 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
804 }
805
806 #[test]
807 fn test_evaluate_missing_field() {
808 let kb = KnowledgeBase::new("test");
809 let executor = RuleExecutor::new(kb);
810
811 let facts = Facts::new(); let condition = Condition::new(
814 "User.Age".to_string(),
815 Operator::GreaterThan,
816 Value::Number(18.0),
817 );
818
819 let result = executor.evaluate_condition(&condition, &facts).unwrap();
820 assert!(!result); }
822
823 #[test]
824 fn test_execute_multiple_actions() {
825 let kb = KnowledgeBase::new("test");
826 let executor = RuleExecutor::new(kb);
827
828 let mut facts = Facts::new();
829 facts.set("User.Points", Value::Number(150.0));
830
831 let conditions = ConditionGroup::Single(Condition::new(
832 "User.Points".to_string(),
833 Operator::GreaterThan,
834 Value::Number(100.0),
835 ));
836
837 let actions = vec![
838 ActionType::Set {
839 field: "User.IsVIP".to_string(),
840 value: Value::Boolean(true),
841 },
842 ActionType::Log {
843 message: "User promoted to VIP".to_string(),
844 },
845 ActionType::Set {
846 field: "User.Discount".to_string(),
847 value: Value::Number(0.2),
848 },
849 ];
850
851 let rule = Rule::new("PromoteToVIP".to_string(), conditions, actions);
852
853 let executed = executor.try_execute_rule(&rule, &mut facts).unwrap();
854 assert!(executed);
855 assert_eq!(facts.get("User.IsVIP"), Some(Value::Boolean(true)));
856 assert_eq!(facts.get("User.Discount"), Some(Value::Number(0.2)));
857 }
858
859 #[test]
860 fn test_evaluate_endswith_operator() {
861 let kb = KnowledgeBase::new("test");
862 let executor = RuleExecutor::new(kb);
863
864 let facts = Facts::new();
865 facts.set("User.Email", Value::String("user@example.com".to_string()));
866 facts.set("File.Name", Value::String("document.pdf".to_string()));
867 facts.set(
868 "Domain.URL",
869 Value::String("https://api.example.org".to_string()),
870 );
871
872 let condition = Condition::new(
874 "User.Email".to_string(),
875 Operator::EndsWith,
876 Value::String(".com".to_string()),
877 );
878 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
879
880 let condition = Condition::new(
882 "File.Name".to_string(),
883 Operator::EndsWith,
884 Value::String(".pdf".to_string()),
885 );
886 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
887
888 let condition = Condition::new(
890 "Domain.URL".to_string(),
891 Operator::EndsWith,
892 Value::String(".org".to_string()),
893 );
894 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
895
896 let condition = Condition::new(
898 "User.Email".to_string(),
899 Operator::EndsWith,
900 Value::String(".net".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("document.pdf".to_string()),
909 );
910 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
911 }
912
913 #[test]
914 fn test_evaluate_endswith_edge_cases() {
915 let kb = KnowledgeBase::new("test");
916 let executor = RuleExecutor::new(kb);
917
918 let facts = Facts::new();
919 facts.set("Empty.String", Value::String("".to_string()));
920 facts.set("Single.Char", Value::String("a".to_string()));
921 facts.set("Number.Value", Value::Number(123.0));
922
923 let condition = Condition::new(
925 "Empty.String".to_string(),
926 Operator::EndsWith,
927 Value::String("".to_string()),
928 );
929 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
930
931 let condition = Condition::new(
933 "Single.Char".to_string(),
934 Operator::EndsWith,
935 Value::String("a".to_string()),
936 );
937 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
938
939 let condition = Condition::new(
941 "Number.Value".to_string(),
942 Operator::EndsWith,
943 Value::String(".0".to_string()),
944 );
945 assert!(!executor.evaluate_condition(&condition, &facts).unwrap());
946
947 let condition = Condition::new(
949 "Missing.Field".to_string(),
950 Operator::EndsWith,
951 Value::String("test".to_string()),
952 );
953 assert!(!executor.evaluate_condition(&condition, &facts).unwrap());
954
955 let facts2 = Facts::new();
957 facts2.set("Text.Value", Value::String("HelloWorld".to_string()));
958
959 let condition = Condition::new(
960 "Text.Value".to_string(),
961 Operator::EndsWith,
962 Value::String("world".to_string()),
963 );
964 assert!(!executor.evaluate_condition(&condition, &facts2).unwrap()); let condition = Condition::new(
967 "Text.Value".to_string(),
968 Operator::EndsWith,
969 Value::String("World".to_string()),
970 );
971 assert!(executor.evaluate_condition(&condition, &facts2).unwrap()); }
973
974 #[test]
975 fn test_evaluate_matches_operator() {
976 let kb = KnowledgeBase::new("test");
977 let executor = RuleExecutor::new(kb);
978
979 let facts = Facts::new();
980 facts.set("User.Email", Value::String("user@example.com".to_string()));
981 facts.set(
982 "Product.Name",
983 Value::String("Premium Laptop Model X".to_string()),
984 );
985 facts.set(
986 "Log.Message",
987 Value::String("Error: Connection timeout".to_string()),
988 );
989
990 let condition = Condition::new(
992 "User.Email".to_string(),
993 Operator::Matches,
994 Value::String("example".to_string()),
995 );
996 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
997
998 let condition = Condition::new(
1000 "Product.Name".to_string(),
1001 Operator::Matches,
1002 Value::String("Premium".to_string()),
1003 );
1004 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
1005
1006 let condition = Condition::new(
1008 "Log.Message".to_string(),
1009 Operator::Matches,
1010 Value::String("Error".to_string()),
1011 );
1012 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
1013
1014 let condition = Condition::new(
1016 "User.Email".to_string(),
1017 Operator::Matches,
1018 Value::String("notfound".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("Laptop".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: Connection timeout".to_string()),
1035 );
1036 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
1037 }
1038
1039 #[test]
1040 fn test_evaluate_matches_edge_cases() {
1041 let kb = KnowledgeBase::new("test");
1042 let executor = RuleExecutor::new(kb);
1043
1044 let facts = Facts::new();
1045 facts.set("Empty.String", Value::String("".to_string()));
1046 facts.set("Single.Char", Value::String("x".to_string()));
1047 facts.set("Number.Value", Value::Number(456.0));
1048 facts.set("Special.Chars", Value::String("test@#$%^&*()".to_string()));
1049
1050 let condition = Condition::new(
1052 "Empty.String".to_string(),
1053 Operator::Matches,
1054 Value::String("".to_string()),
1055 );
1056 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
1057
1058 let condition = Condition::new(
1060 "Single.Char".to_string(),
1061 Operator::Matches,
1062 Value::String("x".to_string()),
1063 );
1064 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
1065
1066 let condition = Condition::new(
1068 "Number.Value".to_string(),
1069 Operator::Matches,
1070 Value::String("456".to_string()),
1071 );
1072 assert!(!executor.evaluate_condition(&condition, &facts).unwrap());
1073
1074 let condition = Condition::new(
1076 "Missing.Field".to_string(),
1077 Operator::Matches,
1078 Value::String("pattern".to_string()),
1079 );
1080 assert!(!executor.evaluate_condition(&condition, &facts).unwrap());
1081
1082 let condition = Condition::new(
1084 "Special.Chars".to_string(),
1085 Operator::Matches,
1086 Value::String("@#$".to_string()),
1087 );
1088 assert!(executor.evaluate_condition(&condition, &facts).unwrap());
1089
1090 let facts2 = Facts::new();
1092 facts2.set("Text.Value", Value::String("HelloWorld".to_string()));
1093
1094 let condition = Condition::new(
1095 "Text.Value".to_string(),
1096 Operator::Matches,
1097 Value::String("hello".to_string()),
1098 );
1099 assert!(!executor.evaluate_condition(&condition, &facts2).unwrap()); let condition = Condition::new(
1102 "Text.Value".to_string(),
1103 Operator::Matches,
1104 Value::String("Hello".to_string()),
1105 );
1106 assert!(executor.evaluate_condition(&condition, &facts2).unwrap()); }
1108
1109 #[test]
1110 fn test_endswith_matches_in_rules() {
1111 let kb = KnowledgeBase::new("test");
1113
1114 let condition1 = Condition::new(
1116 "User.Email".to_string(),
1117 Operator::EndsWith,
1118 Value::String(".edu".to_string()),
1119 );
1120 let actions1 = vec![ActionType::Set {
1121 field: "User.IsStudent".to_string(),
1122 value: Value::Boolean(true),
1123 }];
1124 let rule1 = Rule::new(
1125 "StudentEmailRule".to_string(),
1126 ConditionGroup::Single(condition1),
1127 actions1,
1128 );
1129
1130 let condition2 = Condition::new(
1132 "Product.Name".to_string(),
1133 Operator::Matches,
1134 Value::String("Premium".to_string()),
1135 );
1136 let actions2 = vec![ActionType::Set {
1137 field: "Product.IsPremium".to_string(),
1138 value: Value::Boolean(true),
1139 }];
1140 let rule2 = Rule::new(
1141 "PremiumProductRule".to_string(),
1142 ConditionGroup::Single(condition2),
1143 actions2,
1144 );
1145
1146 let _ = kb.add_rule(rule1.clone());
1147 let _ = kb.add_rule(rule2.clone());
1148
1149 let executor = RuleExecutor::new(kb);
1150
1151 let mut facts1 = Facts::new();
1153 facts1.set(
1154 "User.Email",
1155 Value::String("student@university.edu".to_string()),
1156 );
1157
1158 let executed = executor.try_execute_rule(&rule1, &mut facts1).unwrap();
1159 assert!(executed);
1160 assert_eq!(facts1.get("User.IsStudent"), Some(Value::Boolean(true)));
1161
1162 let mut facts2 = Facts::new();
1164 facts2.set(
1165 "Product.Name",
1166 Value::String("Premium Laptop X1".to_string()),
1167 );
1168
1169 let executed = executor.try_execute_rule(&rule2, &mut facts2).unwrap();
1170 assert!(executed);
1171 assert_eq!(facts2.get("Product.IsPremium"), Some(Value::Boolean(true)));
1172
1173 let mut facts3 = Facts::new();
1175 facts3.set("User.Email", Value::String("user@company.com".to_string()));
1176
1177 let executed = executor.try_execute_rule(&rule1, &mut facts3).unwrap();
1178 assert!(!executed); }
1180}