1#![allow(clippy::type_complexity)]
2
3use crate::rete::alpha::AlphaNode;
4use std::sync::Arc;
5pub fn build_rete_ul_from_condition_group(
7 group: &crate::rete::auto_network::ConditionGroup,
8) -> ReteUlNode {
9 use crate::rete::auto_network::ConditionGroup;
10 match group {
11 ConditionGroup::Single(cond) => ReteUlNode::UlAlpha(AlphaNode {
12 field: cond.field.clone(),
13 operator: cond.operator.clone(),
14 value: cond.value.clone(),
15 }),
16 ConditionGroup::Compound {
17 left,
18 operator,
19 right,
20 } => match operator.as_str() {
21 "AND" => ReteUlNode::UlAnd(
22 Box::new(build_rete_ul_from_condition_group(left)),
23 Box::new(build_rete_ul_from_condition_group(right)),
24 ),
25 "OR" => ReteUlNode::UlOr(
26 Box::new(build_rete_ul_from_condition_group(left)),
27 Box::new(build_rete_ul_from_condition_group(right)),
28 ),
29 _ => ReteUlNode::UlAnd(
30 Box::new(build_rete_ul_from_condition_group(left)),
31 Box::new(build_rete_ul_from_condition_group(right)),
32 ),
33 },
34 ConditionGroup::Not(inner) => {
35 ReteUlNode::UlNot(Box::new(build_rete_ul_from_condition_group(inner)))
36 }
37 ConditionGroup::Exists(inner) => {
38 ReteUlNode::UlExists(Box::new(build_rete_ul_from_condition_group(inner)))
39 }
40 ConditionGroup::Forall(inner) => {
41 ReteUlNode::UlForall(Box::new(build_rete_ul_from_condition_group(inner)))
42 }
43 }
44}
45use std::collections::HashMap;
46
47fn evaluate_condition_string(condition: &str, facts: &HashMap<String, String>) -> bool {
49 let condition = condition.trim();
50 let operators = ["==", "!=", ">=", "<=", ">", "<"];
51
52 for op in &operators {
53 if let Some(pos) = condition.find(op) {
54 let field = condition[..pos].trim();
55 let value_str = condition[pos + op.len()..]
56 .trim()
57 .trim_matches('"')
58 .trim_matches('\'');
59
60 if let Some(field_value) = facts.get(field) {
61 return compare_string_values(field_value, op, value_str);
62 } else {
63 return false;
64 }
65 }
66 }
67 false
68}
69
70fn compare_string_values(field_value: &str, operator: &str, value_str: &str) -> bool {
72 if let (Ok(field_num), Ok(val_num)) = (field_value.parse::<f64>(), value_str.parse::<f64>()) {
74 match operator {
75 "==" => (field_num - val_num).abs() < f64::EPSILON,
76 "!=" => (field_num - val_num).abs() >= f64::EPSILON,
77 ">" => field_num > val_num,
78 "<" => field_num < val_num,
79 ">=" => field_num >= val_num,
80 "<=" => field_num <= val_num,
81 _ => false,
82 }
83 } else {
84 match operator {
86 "==" => field_value == value_str,
87 "!=" => field_value != value_str,
88 _ => false,
89 }
90 }
91}
92
93pub fn evaluate_rete_ul_node(node: &ReteUlNode, facts: &HashMap<String, String>) -> bool {
95 match node {
96 ReteUlNode::UlAlpha(alpha) => {
97 let val = if alpha.field.contains('.') {
98 let parts: Vec<&str> = alpha.field.split('.').collect();
99 if parts.len() == 2 {
100 let prefix = parts[0];
101 let suffix = parts[1];
102 facts
103 .get(&format!("{}.{}", prefix, suffix))
104 .or_else(|| facts.get(&format!("{}:{}", prefix, suffix)))
105 } else {
106 facts.get(&alpha.field)
107 }
108 } else {
109 facts.get(&alpha.field)
110 };
111 if let Some(val) = val {
112 match alpha.operator.as_str() {
113 "==" => val == &alpha.value,
114 "!=" => val != &alpha.value,
115 ">" => {
116 val.parse::<f64>().unwrap_or(0.0)
117 > alpha.value.parse::<f64>().unwrap_or(0.0)
118 }
119 "<" => {
120 val.parse::<f64>().unwrap_or(0.0)
121 < alpha.value.parse::<f64>().unwrap_or(0.0)
122 }
123 ">=" => {
124 val.parse::<f64>().unwrap_or(0.0)
125 >= alpha.value.parse::<f64>().unwrap_or(0.0)
126 }
127 "<=" => {
128 val.parse::<f64>().unwrap_or(0.0)
129 <= alpha.value.parse::<f64>().unwrap_or(0.0)
130 }
131 _ => false,
132 }
133 } else {
134 false
135 }
136 }
137 ReteUlNode::UlAnd(left, right) => {
138 evaluate_rete_ul_node(left, facts) && evaluate_rete_ul_node(right, facts)
139 }
140 ReteUlNode::UlOr(left, right) => {
141 evaluate_rete_ul_node(left, facts) || evaluate_rete_ul_node(right, facts)
142 }
143 ReteUlNode::UlNot(inner) => !evaluate_rete_ul_node(inner, facts),
144 ReteUlNode::UlExists(inner) => {
145 let target_field = match &**inner {
146 ReteUlNode::UlAlpha(alpha) => alpha.field.clone(),
147 _ => "".to_string(),
148 };
149 if target_field.contains('.') {
150 let parts: Vec<&str> = target_field.split('.').collect();
151 if parts.len() == 2 {
152 let prefix = parts[0];
153 let suffix = parts[1];
154 let filtered: Vec<_> = facts
155 .iter()
156 .filter(|(k, _)| k.starts_with(prefix) && k.ends_with(suffix))
157 .collect();
158 filtered.iter().any(|(_, value)| {
159 let mut sub_facts = HashMap::new();
160 sub_facts.insert(target_field.clone(), (*value).clone());
161 evaluate_rete_ul_node(inner, &sub_facts)
162 })
163 } else {
164 facts.iter().any(|(field, value)| {
165 let mut sub_facts = HashMap::new();
166 sub_facts.insert(field.clone(), value.clone());
167 evaluate_rete_ul_node(inner, &sub_facts)
168 })
169 }
170 } else {
171 facts.iter().any(|(field, value)| {
172 let mut sub_facts = HashMap::new();
173 sub_facts.insert(field.clone(), value.clone());
174 evaluate_rete_ul_node(inner, &sub_facts)
175 })
176 }
177 }
178 ReteUlNode::UlForall(inner) => {
179 let target_field = match &**inner {
180 ReteUlNode::UlAlpha(alpha) => alpha.field.clone(),
181 _ => "".to_string(),
182 };
183 if target_field.contains('.') {
184 let parts: Vec<&str> = target_field.split('.').collect();
185 if parts.len() == 2 {
186 let prefix = parts[0];
187 let suffix = parts[1];
188 let filtered: Vec<_> = facts
189 .iter()
190 .filter(|(k, _)| k.starts_with(prefix) && k.ends_with(suffix))
191 .collect();
192 if filtered.is_empty() {
193 return true; }
195 filtered.iter().all(|(_, value)| {
196 let mut sub_facts = HashMap::new();
197 sub_facts.insert(target_field.clone(), (*value).clone());
198 evaluate_rete_ul_node(inner, &sub_facts)
199 })
200 } else {
201 facts.iter().all(|(field, value)| {
202 let mut sub_facts = HashMap::new();
203 sub_facts.insert(field.clone(), value.clone());
204 evaluate_rete_ul_node(inner, &sub_facts)
205 })
206 }
207 } else {
208 facts.iter().all(|(field, value)| {
209 let mut sub_facts = HashMap::new();
210 sub_facts.insert(field.clone(), value.clone());
211 evaluate_rete_ul_node(inner, &sub_facts)
212 })
213 }
214 }
215 ReteUlNode::UlAccumulate {
216 source_pattern,
217 extract_field,
218 source_conditions,
219 function,
220 ..
221 } => {
222 let pattern_prefix = format!("{}.", source_pattern);
225 let mut matching_values = Vec::new();
226
227 let mut instances: std::collections::HashMap<
229 String,
230 std::collections::HashMap<String, String>,
231 > = std::collections::HashMap::new();
232
233 for (key, value) in facts {
234 if key.starts_with(&pattern_prefix) {
235 let parts: Vec<&str> = key
236 .strip_prefix(&pattern_prefix)
237 .unwrap()
238 .split('.')
239 .collect();
240
241 if parts.len() >= 2 {
242 let instance_id = parts[0];
243 let field_name = parts[1..].join(".");
244
245 instances
246 .entry(instance_id.to_string())
247 .or_default()
248 .insert(field_name, value.clone());
249 } else if parts.len() == 1 {
250 instances
251 .entry("default".to_string())
252 .or_default()
253 .insert(parts[0].to_string(), value.clone());
254 }
255 }
256 }
257
258 for (_instance_id, instance_facts) in instances {
260 let mut matches = true;
261
262 for condition_str in source_conditions {
263 if !evaluate_condition_string(condition_str, &instance_facts) {
264 matches = false;
265 break;
266 }
267 }
268
269 if matches {
270 if let Some(value_str) = instance_facts.get(extract_field) {
271 let fact_value = if let Ok(i) = value_str.parse::<i64>() {
273 super::facts::FactValue::Integer(i)
274 } else if let Ok(f) = value_str.parse::<f64>() {
275 super::facts::FactValue::Float(f)
276 } else if let Ok(b) = value_str.parse::<bool>() {
277 super::facts::FactValue::Boolean(b)
278 } else {
279 super::facts::FactValue::String(value_str.clone())
280 };
281 matching_values.push(fact_value);
282 }
283 }
284 }
285
286 let has_results = !matching_values.is_empty();
288
289 match function.as_str() {
290 "count" => has_results, "sum" | "average" | "min" | "max" => {
292 has_results
294 }
295 _ => true, }
297 }
298 ReteUlNode::UlMultiField {
299 field,
300 operation,
301 value,
302 operator,
303 compare_value,
304 } => {
305 let field_value = facts.get(field);
309
310 match operation.as_str() {
311 "empty" => {
312 field_value
314 .map(|v| v.is_empty() || v == "[]")
315 .unwrap_or(true)
316 }
317 "not_empty" => {
318 field_value
320 .map(|v| !v.is_empty() && v != "[]")
321 .unwrap_or(false)
322 }
323 "count" => {
324 if let Some(val) = field_value {
325 let count = if val.starts_with('[') && val.ends_with(']') {
328 let inner = &val[1..val.len() - 1];
329 if inner.trim().is_empty() {
330 0
331 } else {
332 inner.split(',').count()
333 }
334 } else {
335 0
336 };
337
338 if let (Some(op), Some(cmp_val)) = (operator, compare_value) {
340 let cmp_num = cmp_val.parse::<i64>().unwrap_or(0);
341 match op.as_str() {
342 ">" => (count as i64) > cmp_num,
343 "<" => (count as i64) < cmp_num,
344 ">=" => (count as i64) >= cmp_num,
345 "<=" => (count as i64) <= cmp_num,
346 "==" => (count as i64) == cmp_num,
347 "!=" => (count as i64) != cmp_num,
348 _ => false,
349 }
350 } else {
351 count > 0
352 }
353 } else {
354 false
355 }
356 }
357 "contains" => {
358 if let (Some(val), Some(search)) = (field_value, value) {
359 val.contains(search)
361 } else {
362 false
363 }
364 }
365 _ => {
366 false
368 }
369 }
370 }
371 #[cfg(feature = "streaming")]
372 ReteUlNode::UlStream { .. } => {
373 true
376 }
377 ReteUlNode::UlFunctionCall { .. } => false, ReteUlNode::UlTerminal(_) => true,
379 }
380}
381
382#[derive(Debug, Clone)]
384pub enum ReteUlNode {
385 UlAlpha(AlphaNode),
386 UlAnd(Box<ReteUlNode>, Box<ReteUlNode>),
387 UlOr(Box<ReteUlNode>, Box<ReteUlNode>),
388 UlNot(Box<ReteUlNode>),
389 UlExists(Box<ReteUlNode>),
390 UlForall(Box<ReteUlNode>),
391 UlAccumulate {
392 result_var: String,
393 source_pattern: String,
394 extract_field: String,
395 source_conditions: Vec<String>,
396 function: String,
397 function_arg: String,
398 },
399 UlMultiField {
400 field: String,
401 operation: String, value: Option<String>, operator: Option<String>, compare_value: Option<String>, },
406 #[cfg(feature = "streaming")]
407 UlStream {
408 var_name: String,
409 event_type: Option<String>,
410 stream_name: String,
411 window: Option<StreamWindowSpec>,
412 },
413 UlFunctionCall {
416 name: String,
417 args: Vec<String>,
418 operator: String,
419 value: String,
420 },
421 UlTerminal(String), }
423
424#[cfg(feature = "streaming")]
425#[derive(Debug, Clone, PartialEq)]
426pub struct StreamWindowSpec {
427 pub duration: std::time::Duration,
428 pub window_type: StreamWindowTypeRete,
429}
430
431#[cfg(feature = "streaming")]
432#[derive(Debug, Clone, PartialEq)]
433pub enum StreamWindowTypeRete {
434 Sliding,
435 Tumbling,
436 Session { timeout: std::time::Duration },
437}
438
439impl ReteUlNode {
440 pub fn evaluate_typed(&self, facts: &super::facts::TypedFacts) -> bool {
442 evaluate_rete_ul_node_typed(self, facts, &std::collections::HashMap::new())
443 }
444}
445
446pub struct ReteUlRule {
448 pub name: String,
449 pub node: ReteUlNode,
450 pub priority: i32,
451 pub no_loop: bool,
452 pub action: Arc<dyn Fn(&mut std::collections::HashMap<String, String>) + Send + Sync>,
453}
454
455pub fn fire_rete_ul_rules(
458 rules: &mut [(
459 String,
460 ReteUlNode,
461 Box<dyn FnMut(&mut std::collections::HashMap<String, String>)>,
462 )],
463 facts: &mut std::collections::HashMap<String, String>,
464) -> Vec<String> {
465 let mut fired_rules = Vec::new();
466 let mut changed = true;
467 while changed {
468 changed = false;
469 for (rule_name, node, action) in rules.iter_mut() {
470 let fired_flag = format!("{}_fired", rule_name);
471 if facts.get(&fired_flag) == Some(&"true".to_string()) {
472 continue;
473 }
474 if evaluate_rete_ul_node(node, facts) {
475 action(facts);
476 facts.insert(fired_flag.clone(), "true".to_string());
477 fired_rules.push(rule_name.clone());
478 changed = true;
479 }
480 }
481 }
482 fired_rules
483}
484
485pub fn fire_rete_ul_rules_with_agenda(
487 rules: &mut [ReteUlRule],
488 facts: &mut std::collections::HashMap<String, String>,
489) -> Vec<String> {
490 let mut fired_rules = Vec::new();
491 let mut fired_flags = std::collections::HashSet::new();
492 let max_iterations = 100; let mut iterations = 0;
494
495 loop {
496 iterations += 1;
497 if iterations > max_iterations {
498 eprintln!(
499 "Warning: RETE engine reached max iterations ({})",
500 max_iterations
501 );
502 break;
503 }
504
505 let mut agenda: Vec<usize> = rules
507 .iter()
508 .enumerate()
509 .filter(|(_, rule)| {
510 if fired_flags.contains(&rule.name) {
512 return false;
513 }
514 evaluate_rete_ul_node(&rule.node, facts)
516 })
517 .map(|(i, _)| i)
518 .collect();
519
520 if agenda.is_empty() {
522 break;
523 }
524
525 agenda.sort_by_key(|&i| -rules[i].priority);
527
528 for &i in &agenda {
530 let rule = &mut rules[i];
531
532 (rule.action)(facts);
534
535 fired_rules.push(rule.name.clone());
537 fired_flags.insert(rule.name.clone());
538
539 let fired_flag = format!("{}_fired", rule.name);
540 facts.insert(fired_flag, "true".to_string());
541 }
542
543 if rules.iter().all(|r| r.no_loop) {
545 break;
546 }
547 }
548
549 fired_rules
550}
551
552pub struct ReteUlEngine {
555 rules: Vec<ReteUlRule>,
556 facts: std::collections::HashMap<String, String>,
557}
558
559impl Default for ReteUlEngine {
560 fn default() -> Self {
561 Self::new()
562 }
563}
564
565impl ReteUlEngine {
566 pub fn new() -> Self {
568 Self {
569 rules: Vec::new(),
570 facts: std::collections::HashMap::new(),
571 }
572 }
573
574 pub fn add_rule_with_action<F>(
576 &mut self,
577 name: String,
578 node: ReteUlNode,
579 priority: i32,
580 no_loop: bool,
581 action: F,
582 ) where
583 F: Fn(&mut std::collections::HashMap<String, String>) + Send + Sync + 'static,
584 {
585 self.rules.push(ReteUlRule {
586 name,
587 node,
588 priority,
589 no_loop,
590 action: Arc::new(action),
591 });
592 }
593
594 pub fn add_rule_from_definition(
596 &mut self,
597 rule: &crate::rete::auto_network::Rule,
598 priority: i32,
599 no_loop: bool,
600 ) {
601 let node = build_rete_ul_from_condition_group(&rule.conditions);
602 let rule_name = rule.name.clone();
603
604 let action = Arc::new(
606 move |facts: &mut std::collections::HashMap<String, String>| {
607 facts.insert(format!("{}_executed", rule_name), "true".to_string());
608 },
609 );
610
611 self.rules.push(ReteUlRule {
612 name: rule.name.clone(),
613 node,
614 priority,
615 no_loop,
616 action,
617 });
618 }
619
620 pub fn set_fact(&mut self, key: String, value: String) {
622 self.facts.insert(key, value);
623 }
624
625 pub fn get_fact(&self, key: &str) -> Option<&String> {
627 self.facts.get(key)
628 }
629
630 pub fn remove_fact(&mut self, key: &str) -> Option<String> {
632 self.facts.remove(key)
633 }
634
635 pub fn get_all_facts(&self) -> &std::collections::HashMap<String, String> {
637 &self.facts
638 }
639
640 pub fn clear_facts(&mut self) {
642 self.facts.clear();
643 }
644
645 pub fn fire_all(&mut self) -> Vec<String> {
647 fire_rete_ul_rules_with_agenda(&mut self.rules, &mut self.facts)
648 }
649
650 pub fn matches(&self, rule_name: &str) -> bool {
652 self.rules
653 .iter()
654 .find(|r| r.name == rule_name)
655 .map(|r| evaluate_rete_ul_node(&r.node, &self.facts))
656 .unwrap_or(false)
657 }
658
659 pub fn get_matching_rules(&self) -> Vec<&str> {
661 self.rules
662 .iter()
663 .filter(|r| evaluate_rete_ul_node(&r.node, &self.facts))
664 .map(|r| r.name.as_str())
665 .collect()
666 }
667
668 pub fn reset_fired_flags(&mut self) {
670 let keys_to_remove: Vec<_> = self
671 .facts
672 .keys()
673 .filter(|k| k.ends_with("_fired") || k.ends_with("_executed"))
674 .cloned()
675 .collect();
676 for key in keys_to_remove {
677 self.facts.remove(&key);
678 }
679 }
680}
681
682use super::facts::{FactValue, TypedFacts};
687
688pub fn evaluate_rete_ul_node_typed(
691 node: &ReteUlNode,
692 facts: &TypedFacts,
693 custom_fns: &std::collections::HashMap<String, super::propagation::ReteCustomFunction>,
694) -> bool {
695 match node {
696 ReteUlNode::UlAlpha(alpha) => alpha.matches_typed(facts),
697 ReteUlNode::UlAnd(left, right) => {
698 evaluate_rete_ul_node_typed(left, facts, custom_fns)
699 && evaluate_rete_ul_node_typed(right, facts, custom_fns)
700 }
701 ReteUlNode::UlOr(left, right) => {
702 evaluate_rete_ul_node_typed(left, facts, custom_fns)
703 || evaluate_rete_ul_node_typed(right, facts, custom_fns)
704 }
705 ReteUlNode::UlNot(inner) => !evaluate_rete_ul_node_typed(inner, facts, custom_fns),
706 ReteUlNode::UlExists(inner) => {
707 let target_field = match &**inner {
708 ReteUlNode::UlAlpha(alpha) => alpha.field.clone(),
709 _ => "".to_string(),
710 };
711 if target_field.contains('.') {
712 let parts: Vec<&str> = target_field.split('.').collect();
713 if parts.len() == 2 {
714 let prefix = parts[0];
715 let suffix = parts[1];
716 let filtered: Vec<_> = facts
717 .get_all()
718 .iter()
719 .filter(|(k, _)| k.starts_with(prefix) && k.ends_with(suffix))
720 .collect();
721 filtered
722 .iter()
723 .any(|(_, _)| evaluate_rete_ul_node_typed(inner, facts, custom_fns))
724 } else {
725 evaluate_rete_ul_node_typed(inner, facts, custom_fns)
726 }
727 } else {
728 evaluate_rete_ul_node_typed(inner, facts, custom_fns)
729 }
730 }
731 ReteUlNode::UlForall(inner) => {
732 let target_field = match &**inner {
733 ReteUlNode::UlAlpha(alpha) => alpha.field.clone(),
734 _ => "".to_string(),
735 };
736 if target_field.contains('.') {
737 let parts: Vec<&str> = target_field.split('.').collect();
738 if parts.len() == 2 {
739 let prefix = parts[0];
740 let suffix = parts[1];
741 let filtered: Vec<_> = facts
742 .get_all()
743 .iter()
744 .filter(|(k, _)| k.starts_with(prefix) && k.ends_with(suffix))
745 .collect();
746 if filtered.is_empty() {
747 return true; }
749 filtered
750 .iter()
751 .all(|(_, _)| evaluate_rete_ul_node_typed(inner, facts, custom_fns))
752 } else {
753 if facts.get_all().is_empty() {
754 return true; }
756 evaluate_rete_ul_node_typed(inner, facts, custom_fns)
757 }
758 } else {
759 if facts.get_all().is_empty() {
760 return true; }
762 evaluate_rete_ul_node_typed(inner, facts, custom_fns)
763 }
764 }
765 ReteUlNode::UlAccumulate {
766 source_pattern,
767 extract_field,
768 source_conditions,
769 function,
770 ..
771 } => {
772 let pattern_prefix = format!("{}.", source_pattern);
775 let mut matching_values = Vec::new();
776
777 let mut instances: std::collections::HashMap<
779 String,
780 std::collections::HashMap<String, FactValue>,
781 > = std::collections::HashMap::new();
782
783 for (key, value) in facts.get_all() {
784 if key.starts_with(&pattern_prefix) {
785 let parts: Vec<&str> = key
786 .strip_prefix(&pattern_prefix)
787 .unwrap()
788 .split('.')
789 .collect();
790
791 if parts.len() >= 2 {
792 let instance_id = parts[0];
793 let field_name = parts[1..].join(".");
794
795 instances
796 .entry(instance_id.to_string())
797 .or_default()
798 .insert(field_name, value.clone());
799 } else if parts.len() == 1 {
800 instances
801 .entry("default".to_string())
802 .or_default()
803 .insert(parts[0].to_string(), value.clone());
804 }
805 }
806 }
807
808 for (_instance_id, instance_facts) in instances {
810 let mut matches = true;
811
812 for condition_str in source_conditions {
813 let string_facts: HashMap<String, String> = instance_facts
815 .iter()
816 .map(|(k, v)| (k.clone(), format!("{:?}", v)))
817 .collect();
818
819 if !evaluate_condition_string(condition_str, &string_facts) {
820 matches = false;
821 break;
822 }
823 }
824
825 if matches {
826 if let Some(value) = instance_facts.get(extract_field) {
827 matching_values.push(value.clone());
828 }
829 }
830 }
831
832 let has_results = !matching_values.is_empty();
834
835 match function.as_str() {
836 "count" => has_results,
837 "sum" | "average" | "min" | "max" => has_results,
838 _ => true,
839 }
840 }
841 ReteUlNode::UlMultiField {
842 field,
843 operation,
844 value,
845 operator,
846 compare_value,
847 } => {
848 use super::facts::FactValue;
850
851 let field_value = facts.get(field);
852
853 match operation.as_str() {
854 "empty" => {
855 if let Some(FactValue::Array(arr)) = field_value {
857 arr.is_empty()
858 } else {
859 true
861 }
862 }
863 "not_empty" => {
864 if let Some(FactValue::Array(arr)) = field_value {
866 !arr.is_empty()
867 } else {
868 false
869 }
870 }
871 "count" => {
872 if let Some(FactValue::Array(arr)) = field_value {
873 let count = arr.len() as i64;
874
875 if let (Some(op), Some(cmp_val)) = (operator, compare_value) {
877 let cmp_num = cmp_val.parse::<i64>().unwrap_or(0);
878 match op.as_str() {
879 ">" => count > cmp_num,
880 "<" => count < cmp_num,
881 ">=" => count >= cmp_num,
882 "<=" => count <= cmp_num,
883 "==" => count == cmp_num,
884 "!=" => count != cmp_num,
885 _ => false,
886 }
887 } else {
888 count > 0
889 }
890 } else {
891 false
892 }
893 }
894 "contains" => {
895 if let (Some(FactValue::Array(arr)), Some(search)) = (field_value, value) {
896 arr.iter().any(|item| match item {
899 FactValue::String(s) => s == search,
900 FactValue::Integer(i) => i.to_string() == *search,
901 FactValue::Float(f) => f.to_string() == *search,
902 FactValue::Boolean(b) => b.to_string() == *search,
903 _ => false,
904 })
905 } else {
906 false
907 }
908 }
909 "first" => {
910 if let Some(FactValue::Array(arr)) = field_value {
912 !arr.is_empty()
913 } else {
914 false
915 }
916 }
917 "last" => {
918 if let Some(FactValue::Array(arr)) = field_value {
920 !arr.is_empty()
921 } else {
922 false
923 }
924 }
925 "collect" => {
926 matches!(field_value, Some(FactValue::Array(_)))
928 }
929 _ => {
930 false
932 }
933 }
934 }
935 ReteUlNode::UlFunctionCall { name, args, operator, value } => {
936 let resolved: Vec<FactValue> = args.iter().map(|arg| {
939 if let Some(v) = facts.get(arg) {
940 return v.clone();
941 }
942 if (arg.starts_with('"') && arg.ends_with('"'))
944 || (arg.starts_with('\'') && arg.ends_with('\''))
945 {
946 return FactValue::String(arg[1..arg.len() - 1].to_string());
947 }
948 if let Ok(n) = arg.parse::<f64>() {
950 return FactValue::from(n);
951 }
952 FactValue::String(arg.clone())
953 }).collect();
954
955 let result = if let Some(func) = custom_fns.get(name) {
956 func(&resolved, facts).unwrap_or(FactValue::Boolean(false))
957 } else {
958 FactValue::Boolean(false)
959 };
960
961 let expected = match value.as_str() {
962 "true" => FactValue::Boolean(true),
963 "false" => FactValue::Boolean(false),
964 s => if let Ok(n) = s.parse::<f64>() {
965 FactValue::from(n)
966 } else {
967 FactValue::String(s.to_string())
968 },
969 };
970
971 result.compare(operator, &expected)
972 }
973 #[cfg(feature = "streaming")]
974 ReteUlNode::UlStream { .. } => {
975 true
978 }
979 ReteUlNode::UlTerminal(_) => true,
980 }
981}
982
983pub struct TypedReteUlRule {
985 pub name: String,
986 pub node: ReteUlNode,
987 pub priority: i32,
988 pub no_loop: bool,
989 pub action: Arc<dyn Fn(&mut TypedFacts, &mut super::ActionResults) + Send + Sync>,
990}
991
992pub struct TypedReteUlEngine {
995 rules: Vec<TypedReteUlRule>,
996 facts: TypedFacts,
997}
998
999impl TypedReteUlEngine {
1000 pub fn new() -> Self {
1002 Self {
1003 rules: Vec::new(),
1004 facts: TypedFacts::new(),
1005 }
1006 }
1007
1008 pub fn add_rule_with_action<F>(
1010 &mut self,
1011 name: String,
1012 node: ReteUlNode,
1013 priority: i32,
1014 no_loop: bool,
1015 action: F,
1016 ) where
1017 F: Fn(&mut TypedFacts, &mut super::ActionResults) + Send + Sync + 'static,
1018 {
1019 self.rules.push(TypedReteUlRule {
1020 name,
1021 node,
1022 priority,
1023 no_loop,
1024 action: Arc::new(action),
1025 });
1026 }
1027
1028 pub fn add_rule_from_definition(
1030 &mut self,
1031 rule: &crate::rete::auto_network::Rule,
1032 priority: i32,
1033 no_loop: bool,
1034 ) {
1035 let node = build_rete_ul_from_condition_group(&rule.conditions);
1036 let rule_name = rule.name.clone();
1037
1038 let action = Arc::new(
1039 move |facts: &mut TypedFacts, _results: &mut super::ActionResults| {
1040 facts.set(format!("{}_executed", rule_name), true);
1041 },
1042 );
1043
1044 self.rules.push(TypedReteUlRule {
1045 name: rule.name.clone(),
1046 node,
1047 priority,
1048 no_loop,
1049 action,
1050 });
1051 }
1052
1053 pub fn set_fact<K: Into<String>, V: Into<FactValue>>(&mut self, key: K, value: V) {
1055 self.facts.set(key, value);
1056 }
1057
1058 pub fn get_fact(&self, key: &str) -> Option<&FactValue> {
1060 self.facts.get(key)
1061 }
1062
1063 pub fn remove_fact(&mut self, key: &str) -> Option<FactValue> {
1065 self.facts.remove(key)
1066 }
1067
1068 pub fn get_all_facts(&self) -> &TypedFacts {
1070 &self.facts
1071 }
1072
1073 pub fn clear_facts(&mut self) {
1075 self.facts.clear();
1076 }
1077
1078 pub fn fire_all(&mut self) -> Vec<String> {
1080 let mut fired_rules = Vec::new();
1081 let mut agenda: Vec<usize>;
1082 let mut changed = true;
1083 let mut fired_flags = std::collections::HashSet::new();
1084
1085 while changed {
1086 changed = false;
1087
1088 agenda = self
1090 .rules
1091 .iter()
1092 .enumerate()
1093 .filter(|(_, rule)| {
1094 let fired_flag = format!("{}_fired", rule.name);
1095 let already_fired = fired_flags.contains(&rule.name)
1096 || self.facts.get(&fired_flag).and_then(|v| v.as_boolean()) == Some(true);
1097 !rule.no_loop || !already_fired
1098 })
1099 .filter(|(_, rule)| evaluate_rete_ul_node_typed(&rule.node, &self.facts, &std::collections::HashMap::new()))
1100 .map(|(i, _)| i)
1101 .collect();
1102
1103 agenda.sort_by_key(|&i| -self.rules[i].priority);
1105
1106 for &i in &agenda {
1107 let rule = &mut self.rules[i];
1108 let fired_flag = format!("{}_fired", rule.name);
1109 let already_fired = fired_flags.contains(&rule.name)
1110 || self.facts.get(&fired_flag).and_then(|v| v.as_boolean()) == Some(true);
1111
1112 if rule.no_loop && already_fired {
1113 continue;
1114 }
1115
1116 let mut action_results = super::ActionResults::new();
1117 (rule.action)(&mut self.facts, &mut action_results);
1118 fired_rules.push(rule.name.clone());
1122 fired_flags.insert(rule.name.clone());
1123 self.facts.set(fired_flag, true);
1124 changed = true;
1125 }
1126 }
1127
1128 fired_rules
1129 }
1130
1131 pub fn matches(&self, rule_name: &str) -> bool {
1133 self.rules
1134 .iter()
1135 .find(|r| r.name == rule_name)
1136 .map(|r| evaluate_rete_ul_node_typed(&r.node, &self.facts, &std::collections::HashMap::new()))
1137 .unwrap_or(false)
1138 }
1139
1140 pub fn get_matching_rules(&self) -> Vec<&str> {
1142 self.rules
1143 .iter()
1144 .filter(|r| evaluate_rete_ul_node_typed(&r.node, &self.facts, &std::collections::HashMap::new()))
1145 .map(|r| r.name.as_str())
1146 .collect()
1147 }
1148
1149 pub fn reset_fired_flags(&mut self) {
1151 let keys_to_remove: Vec<_> = self
1152 .facts
1153 .get_all()
1154 .keys()
1155 .filter(|k| k.ends_with("_fired") || k.ends_with("_executed"))
1156 .cloned()
1157 .collect();
1158 for key in keys_to_remove {
1159 self.facts.remove(&key);
1160 }
1161 }
1162}
1163
1164impl Default for TypedReteUlEngine {
1165 fn default() -> Self {
1166 Self::new()
1167 }
1168}