rust_rule_engine/rete/
network.rs

1use crate::rete::alpha::AlphaNode;
2/// Chuyển ConditionGroup sang ReteUlNode
3pub fn build_rete_ul_from_condition_group(group: &crate::rete::auto_network::ConditionGroup) -> ReteUlNode {
4    use crate::rete::auto_network::ConditionGroup;
5    match group {
6        ConditionGroup::Single(cond) => {
7            ReteUlNode::UlAlpha(AlphaNode {
8                field: cond.field.clone(),
9                operator: cond.operator.clone(),
10                value: cond.value.clone(),
11            })
12        }
13        ConditionGroup::Compound { left, operator, right } => {
14            match operator.as_str() {
15                "AND" => ReteUlNode::UlAnd(
16                    Box::new(build_rete_ul_from_condition_group(left)),
17                    Box::new(build_rete_ul_from_condition_group(right)),
18                ),
19                "OR" => ReteUlNode::UlOr(
20                    Box::new(build_rete_ul_from_condition_group(left)),
21                    Box::new(build_rete_ul_from_condition_group(right)),
22                ),
23                _ => ReteUlNode::UlAnd(
24                    Box::new(build_rete_ul_from_condition_group(left)),
25                    Box::new(build_rete_ul_from_condition_group(right)),
26                ),
27            }
28        }
29        ConditionGroup::Not(inner) => {
30            ReteUlNode::UlNot(Box::new(build_rete_ul_from_condition_group(inner)))
31        }
32        ConditionGroup::Exists(inner) => {
33            ReteUlNode::UlExists(Box::new(build_rete_ul_from_condition_group(inner)))
34        }
35        ConditionGroup::Forall(inner) => {
36            ReteUlNode::UlForall(Box::new(build_rete_ul_from_condition_group(inner)))
37        }
38    }
39}
40use std::collections::HashMap;
41
42/// Đánh giá mạng node RETE với facts
43pub fn evaluate_rete_ul_node(node: &ReteUlNode, facts: &HashMap<String, String>) -> bool {
44    match node {
45        ReteUlNode::UlAlpha(alpha) => {
46            let val = if alpha.field.contains('.') {
47                let parts: Vec<&str> = alpha.field.split('.').collect();
48                if parts.len() == 2 {
49                    let prefix = parts[0];
50                    let suffix = parts[1];
51                    facts.get(&format!("{}.{}", prefix, suffix)).or_else(|| facts.get(&format!("{}:{}", prefix, suffix)))
52                } else {
53                    facts.get(&alpha.field)
54                }
55            } else {
56                facts.get(&alpha.field)
57            };
58            if let Some(val) = val {
59                match alpha.operator.as_str() {
60                    "==" => val == &alpha.value,
61                    "!=" => val != &alpha.value,
62                    ">" => val.parse::<f64>().unwrap_or(0.0) > alpha.value.parse::<f64>().unwrap_or(0.0),
63                    "<" => val.parse::<f64>().unwrap_or(0.0) < alpha.value.parse::<f64>().unwrap_or(0.0),
64                    ">=" => val.parse::<f64>().unwrap_or(0.0) >= alpha.value.parse::<f64>().unwrap_or(0.0),
65                    "<=" => val.parse::<f64>().unwrap_or(0.0) <= alpha.value.parse::<f64>().unwrap_or(0.0),
66                    _ => false,
67                }
68            } else {
69                false
70            }
71        }
72        ReteUlNode::UlAnd(left, right) => {
73            evaluate_rete_ul_node(left, facts) && evaluate_rete_ul_node(right, facts)
74        }
75        ReteUlNode::UlOr(left, right) => {
76            evaluate_rete_ul_node(left, facts) || evaluate_rete_ul_node(right, facts)
77        }
78        ReteUlNode::UlNot(inner) => {
79            !evaluate_rete_ul_node(inner, facts)
80        }
81        ReteUlNode::UlExists(inner) => {
82            let target_field = match &**inner {
83                ReteUlNode::UlAlpha(alpha) => alpha.field.clone(),
84                _ => "".to_string(),
85            };
86            if target_field.contains('.') {
87                let parts: Vec<&str> = target_field.split('.').collect();
88                if parts.len() == 2 {
89                    let prefix = parts[0];
90                    let suffix = parts[1];
91                    let filtered: Vec<_> = facts.iter()
92                        .filter(|(k, _)| k.starts_with(prefix) && k.ends_with(suffix))
93                        .collect();
94                    filtered.iter().any(|(_, value)| {
95                        let mut sub_facts = HashMap::new();
96                        sub_facts.insert(target_field.clone(), (*value).clone());
97                        evaluate_rete_ul_node(inner, &sub_facts)
98                    })
99                } else {
100                    facts.iter().any(|(field, value)| {
101                        let mut sub_facts = HashMap::new();
102                        sub_facts.insert(field.clone(), value.clone());
103                        evaluate_rete_ul_node(inner, &sub_facts)
104                    })
105                }
106            } else {
107                facts.iter().any(|(field, value)| {
108                    let mut sub_facts = HashMap::new();
109                    sub_facts.insert(field.clone(), value.clone());
110                    evaluate_rete_ul_node(inner, &sub_facts)
111                })
112            }
113        }
114        ReteUlNode::UlForall(inner) => {
115            let target_field = match &**inner {
116                ReteUlNode::UlAlpha(alpha) => alpha.field.clone(),
117                _ => "".to_string(),
118            };
119            if target_field.contains('.') {
120                let parts: Vec<&str> = target_field.split('.').collect();
121                if parts.len() == 2 {
122                    let prefix = parts[0];
123                    let suffix = parts[1];
124                    let filtered: Vec<_> = facts.iter()
125                        .filter(|(k, _)| k.starts_with(prefix) && k.ends_with(suffix))
126                        .collect();
127                    if filtered.is_empty() {
128                        return true; // Vacuous truth: FORALL on empty set is TRUE
129                    }
130                    filtered.iter().all(|(_, value)| {
131                        let mut sub_facts = HashMap::new();
132                        sub_facts.insert(target_field.clone(), (*value).clone());
133                        evaluate_rete_ul_node(inner, &sub_facts)
134                    })
135                } else {
136                    facts.iter().all(|(field, value)| {
137                        let mut sub_facts = HashMap::new();
138                        sub_facts.insert(field.clone(), value.clone());
139                        evaluate_rete_ul_node(inner, &sub_facts)
140                    })
141                }
142            } else {
143                facts.iter().all(|(field, value)| {
144                    let mut sub_facts = HashMap::new();
145                    sub_facts.insert(field.clone(), value.clone());
146                    evaluate_rete_ul_node(inner, &sub_facts)
147                })
148            }
149        }
150        ReteUlNode::UlTerminal(_) => true // Rule match
151    }
152}
153
154/// RETE-UL: Unified Logic Node
155#[derive(Debug, Clone)]
156pub enum ReteUlNode {
157    UlAlpha(AlphaNode),
158    UlAnd(Box<ReteUlNode>, Box<ReteUlNode>),
159    UlOr(Box<ReteUlNode>, Box<ReteUlNode>),
160    UlNot(Box<ReteUlNode>),
161    UlExists(Box<ReteUlNode>),
162    UlForall(Box<ReteUlNode>),
163    UlTerminal(String), // Rule name
164}
165
166impl ReteUlNode {
167    /// Evaluate with typed facts (convenience method)
168    pub fn evaluate_typed(&self, facts: &super::facts::TypedFacts) -> bool {
169        evaluate_rete_ul_node_typed(self, facts)
170    }
171}
172
173/// RETE-UL Rule Struct
174pub struct ReteUlRule {
175    pub name: String,
176    pub node: ReteUlNode,
177    pub priority: i32,
178    pub no_loop: bool,
179    pub action: Box<dyn FnMut(&mut std::collections::HashMap<String, String>)>,
180}
181
182/// Drools-style RETE-UL rule firing loop
183/// Fires all matching rules, updates facts, repeats until no more rules can fire
184pub fn fire_rete_ul_rules(
185    rules: &mut [(String, ReteUlNode, Box<dyn FnMut(&mut std::collections::HashMap<String, String>)>)],
186    facts: &mut std::collections::HashMap<String, String>,
187) -> Vec<String> {
188    let mut fired_rules = Vec::new();
189    let mut changed = true;
190    while changed {
191        changed = false;
192        for (rule_name, node, action) in rules.iter_mut() {
193            let fired_flag = format!("{}_fired", rule_name);
194            if facts.get(&fired_flag) == Some(&"true".to_string()) {
195                continue;
196            }
197            if evaluate_rete_ul_node(node, facts) {
198                action(facts);
199                facts.insert(fired_flag.clone(), "true".to_string());
200                fired_rules.push(rule_name.clone());
201                changed = true;
202            }
203        }
204    }
205    fired_rules
206}
207
208/// Drools-style RETE-UL rule firing loop with agenda and control
209pub fn fire_rete_ul_rules_with_agenda(
210    rules: &mut [ReteUlRule],
211    facts: &mut std::collections::HashMap<String, String>,
212) -> Vec<String> {
213    let mut fired_rules = Vec::new();
214    let mut fired_flags = std::collections::HashSet::new();
215    let max_iterations = 100; // Prevent infinite loops
216    let mut iterations = 0;
217
218    loop {
219        iterations += 1;
220        if iterations > max_iterations {
221            eprintln!("Warning: RETE engine reached max iterations ({})", max_iterations);
222            break;
223        }
224
225        // Build agenda: rules that match and haven't been fired yet
226        let mut agenda: Vec<usize> = rules
227            .iter()
228            .enumerate()
229            .filter(|(_, rule)| {
230                // Check if rule already fired
231                if fired_flags.contains(&rule.name) {
232                    return false;
233                }
234                // Check if rule matches current facts
235                evaluate_rete_ul_node(&rule.node, facts)
236            })
237            .map(|(i, _)| i)
238            .collect();
239
240        // If no rules to fire, we're done
241        if agenda.is_empty() {
242            break;
243        }
244
245        // Sort agenda by priority (descending)
246        agenda.sort_by_key(|&i| -rules[i].priority);
247
248        // Fire all rules in agenda
249        for &i in &agenda {
250            let rule = &mut rules[i];
251
252            // Execute rule action
253            (rule.action)(facts);
254
255            // Mark as fired
256            fired_rules.push(rule.name.clone());
257            fired_flags.insert(rule.name.clone());
258
259            let fired_flag = format!("{}_fired", rule.name);
260            facts.insert(fired_flag, "true".to_string());
261        }
262
263        // If no_loop is enabled for all rules, stop after one iteration
264        if rules.iter().all(|r| r.no_loop) {
265            break;
266        }
267    }
268
269    fired_rules
270}
271
272/// RETE-UL Engine with cached nodes (Performance optimized!)
273/// This engine builds RETE nodes once and reuses them, avoiding expensive rebuilds
274pub struct ReteUlEngine {
275    rules: Vec<ReteUlRule>,
276    facts: std::collections::HashMap<String, String>,
277}
278
279impl ReteUlEngine {
280    /// Create new engine from Rule definitions (nodes are built and cached once)
281    pub fn new() -> Self {
282        Self {
283            rules: Vec::new(),
284            facts: std::collections::HashMap::new(),
285        }
286    }
287
288    /// Add a rule with custom action closure
289    pub fn add_rule_with_action<F>(
290        &mut self,
291        name: String,
292        node: ReteUlNode,
293        priority: i32,
294        no_loop: bool,
295        action: F,
296    ) where
297        F: FnMut(&mut std::collections::HashMap<String, String>) + 'static,
298    {
299        self.rules.push(ReteUlRule {
300            name,
301            node,
302            priority,
303            no_loop,
304            action: Box::new(action),
305        });
306    }
307
308    /// Add a rule from Rule definition (auto-build node once and cache)
309    pub fn add_rule_from_definition(
310        &mut self,
311        rule: &crate::rete::auto_network::Rule,
312        priority: i32,
313        no_loop: bool,
314    ) {
315        let node = build_rete_ul_from_condition_group(&rule.conditions);
316        let rule_name = rule.name.clone();
317
318        // Default action: just mark as fired
319        let action = Box::new(move |facts: &mut std::collections::HashMap<String, String>| {
320            facts.insert(format!("{}_executed", rule_name), "true".to_string());
321        }) as Box<dyn FnMut(&mut std::collections::HashMap<String, String>)>;
322
323        self.rules.push(ReteUlRule {
324            name: rule.name.clone(),
325            node,
326            priority,
327            no_loop,
328            action,
329        });
330    }
331
332    /// Set a fact
333    pub fn set_fact(&mut self, key: String, value: String) {
334        self.facts.insert(key, value);
335    }
336
337    /// Get a fact
338    pub fn get_fact(&self, key: &str) -> Option<&String> {
339        self.facts.get(key)
340    }
341
342    /// Remove a fact
343    pub fn remove_fact(&mut self, key: &str) -> Option<String> {
344        self.facts.remove(key)
345    }
346
347    /// Get all facts
348    pub fn get_all_facts(&self) -> &std::collections::HashMap<String, String> {
349        &self.facts
350    }
351
352    /// Clear all facts
353    pub fn clear_facts(&mut self) {
354        self.facts.clear();
355    }
356
357    /// Fire all rules with agenda (using cached nodes - NO rebuild!)
358    pub fn fire_all(&mut self) -> Vec<String> {
359        fire_rete_ul_rules_with_agenda(&mut self.rules, &mut self.facts)
360    }
361
362    /// Check if a specific rule matches current facts (without firing)
363    pub fn matches(&self, rule_name: &str) -> bool {
364        self.rules
365            .iter()
366            .find(|r| r.name == rule_name)
367            .map(|r| evaluate_rete_ul_node(&r.node, &self.facts))
368            .unwrap_or(false)
369    }
370
371    /// Get all matching rules (without firing)
372    pub fn get_matching_rules(&self) -> Vec<&str> {
373        self.rules
374            .iter()
375            .filter(|r| evaluate_rete_ul_node(&r.node, &self.facts))
376            .map(|r| r.name.as_str())
377            .collect()
378    }
379
380    /// Reset fired flags (allow rules to fire again)
381    pub fn reset_fired_flags(&mut self) {
382        let keys_to_remove: Vec<_> = self.facts
383            .keys()
384            .filter(|k| k.ends_with("_fired") || k.ends_with("_executed"))
385            .cloned()
386            .collect();
387        for key in keys_to_remove {
388            self.facts.remove(&key);
389        }
390    }
391}
392
393// ============================================================================
394// Typed Facts Support (NEW!)
395// ============================================================================
396
397use super::facts::{FactValue, TypedFacts};
398
399/// Evaluate RETE-UL node with typed facts (NEW!)
400pub fn evaluate_rete_ul_node_typed(node: &ReteUlNode, facts: &TypedFacts) -> bool {
401    match node {
402        ReteUlNode::UlAlpha(alpha) => {
403            alpha.matches_typed(facts)
404        }
405        ReteUlNode::UlAnd(left, right) => {
406            evaluate_rete_ul_node_typed(left, facts) && evaluate_rete_ul_node_typed(right, facts)
407        }
408        ReteUlNode::UlOr(left, right) => {
409            evaluate_rete_ul_node_typed(left, facts) || evaluate_rete_ul_node_typed(right, facts)
410        }
411        ReteUlNode::UlNot(inner) => {
412            !evaluate_rete_ul_node_typed(inner, facts)
413        }
414        ReteUlNode::UlExists(inner) => {
415            let target_field = match &**inner {
416                ReteUlNode::UlAlpha(alpha) => alpha.field.clone(),
417                _ => "".to_string(),
418            };
419            if target_field.contains('.') {
420                let parts: Vec<&str> = target_field.split('.').collect();
421                if parts.len() == 2 {
422                    let prefix = parts[0];
423                    let suffix = parts[1];
424                    let filtered: Vec<_> = facts.get_all().iter()
425                        .filter(|(k, _)| k.starts_with(prefix) && k.ends_with(suffix))
426                        .collect();
427                    filtered.iter().any(|(_, _)| {
428                        evaluate_rete_ul_node_typed(inner, facts)
429                    })
430                } else {
431                    evaluate_rete_ul_node_typed(inner, facts)
432                }
433            } else {
434                evaluate_rete_ul_node_typed(inner, facts)
435            }
436        }
437        ReteUlNode::UlForall(inner) => {
438            let target_field = match &**inner {
439                ReteUlNode::UlAlpha(alpha) => alpha.field.clone(),
440                _ => "".to_string(),
441            };
442            if target_field.contains('.') {
443                let parts: Vec<&str> = target_field.split('.').collect();
444                if parts.len() == 2 {
445                    let prefix = parts[0];
446                    let suffix = parts[1];
447                    let filtered: Vec<_> = facts.get_all().iter()
448                        .filter(|(k, _)| k.starts_with(prefix) && k.ends_with(suffix))
449                        .collect();
450                    if filtered.is_empty() {
451                        return true; // Vacuous truth
452                    }
453                    filtered.iter().all(|(_, _)| {
454                        evaluate_rete_ul_node_typed(inner, facts)
455                    })
456                } else {
457                    if facts.get_all().is_empty() {
458                        return true; // Vacuous truth
459                    }
460                    evaluate_rete_ul_node_typed(inner, facts)
461                }
462            } else {
463                if facts.get_all().is_empty() {
464                    return true; // Vacuous truth
465                }
466                evaluate_rete_ul_node_typed(inner, facts)
467            }
468        }
469        ReteUlNode::UlTerminal(_) => true
470    }
471}
472
473/// Typed RETE-UL Rule
474pub struct TypedReteUlRule {
475    pub name: String,
476    pub node: ReteUlNode,
477    pub priority: i32,
478    pub no_loop: bool,
479    pub action: Box<dyn FnMut(&mut TypedFacts)>,
480}
481
482/// Typed RETE-UL Engine with cached nodes (Performance + Type Safety!)
483/// This is the recommended engine for new code
484pub struct TypedReteUlEngine {
485    rules: Vec<TypedReteUlRule>,
486    facts: TypedFacts,
487}
488
489impl TypedReteUlEngine {
490    /// Create new typed engine
491    pub fn new() -> Self {
492        Self {
493            rules: Vec::new(),
494            facts: TypedFacts::new(),
495        }
496    }
497
498    /// Add a rule with custom action
499    pub fn add_rule_with_action<F>(
500        &mut self,
501        name: String,
502        node: ReteUlNode,
503        priority: i32,
504        no_loop: bool,
505        action: F,
506    ) where
507        F: FnMut(&mut TypedFacts) + 'static,
508    {
509        self.rules.push(TypedReteUlRule {
510            name,
511            node,
512            priority,
513            no_loop,
514            action: Box::new(action),
515        });
516    }
517
518    /// Add a rule from Rule definition
519    pub fn add_rule_from_definition(
520        &mut self,
521        rule: &crate::rete::auto_network::Rule,
522        priority: i32,
523        no_loop: bool,
524    ) {
525        let node = build_rete_ul_from_condition_group(&rule.conditions);
526        let rule_name = rule.name.clone();
527
528        let action = Box::new(move |facts: &mut TypedFacts| {
529            facts.set(format!("{}_executed", rule_name), true);
530        }) as Box<dyn FnMut(&mut TypedFacts)>;
531
532        self.rules.push(TypedReteUlRule {
533            name: rule.name.clone(),
534            node,
535            priority,
536            no_loop,
537            action,
538        });
539    }
540
541    /// Set a fact with typed value
542    pub fn set_fact<K: Into<String>, V: Into<FactValue>>(&mut self, key: K, value: V) {
543        self.facts.set(key, value);
544    }
545
546    /// Get a fact
547    pub fn get_fact(&self, key: &str) -> Option<&FactValue> {
548        self.facts.get(key)
549    }
550
551    /// Remove a fact
552    pub fn remove_fact(&mut self, key: &str) -> Option<FactValue> {
553        self.facts.remove(key)
554    }
555
556    /// Get all facts
557    pub fn get_all_facts(&self) -> &TypedFacts {
558        &self.facts
559    }
560
561    /// Clear all facts
562    pub fn clear_facts(&mut self) {
563        self.facts.clear();
564    }
565
566    /// Fire all rules with agenda (using cached nodes + typed evaluation!)
567    pub fn fire_all(&mut self) -> Vec<String> {
568        let mut fired_rules = Vec::new();
569        let mut agenda: Vec<usize>;
570        let mut changed = true;
571        let mut fired_flags = std::collections::HashSet::new();
572
573        while changed {
574            changed = false;
575
576            // Build agenda: rules that match and not fired
577            agenda = self.rules.iter().enumerate()
578                .filter(|(_, rule)| {
579                    let fired_flag = format!("{}_fired", rule.name);
580                    let already_fired = fired_flags.contains(&rule.name) ||
581                        self.facts.get(&fired_flag).and_then(|v| v.as_boolean()) == Some(true);
582                    !rule.no_loop || !already_fired
583                })
584                .filter(|(_, rule)| evaluate_rete_ul_node_typed(&rule.node, &self.facts))
585                .map(|(i, _)| i)
586                .collect();
587
588            // Sort by priority (descending)
589            agenda.sort_by_key(|&i| -self.rules[i].priority);
590
591            for &i in &agenda {
592                let rule = &mut self.rules[i];
593                let fired_flag = format!("{}_fired", rule.name);
594                let already_fired = fired_flags.contains(&rule.name) ||
595                    self.facts.get(&fired_flag).and_then(|v| v.as_boolean()) == Some(true);
596
597                if rule.no_loop && already_fired {
598                    continue;
599                }
600
601                (rule.action)(&mut self.facts);
602                fired_rules.push(rule.name.clone());
603                fired_flags.insert(rule.name.clone());
604                self.facts.set(fired_flag, true);
605                changed = true;
606            }
607        }
608
609        fired_rules
610    }
611
612    /// Check if a specific rule matches current facts
613    pub fn matches(&self, rule_name: &str) -> bool {
614        self.rules
615            .iter()
616            .find(|r| r.name == rule_name)
617            .map(|r| evaluate_rete_ul_node_typed(&r.node, &self.facts))
618            .unwrap_or(false)
619    }
620
621    /// Get all matching rules
622    pub fn get_matching_rules(&self) -> Vec<&str> {
623        self.rules
624            .iter()
625            .filter(|r| evaluate_rete_ul_node_typed(&r.node, &self.facts))
626            .map(|r| r.name.as_str())
627            .collect()
628    }
629
630    /// Reset fired flags
631    pub fn reset_fired_flags(&mut self) {
632        let keys_to_remove: Vec<_> = self.facts.get_all()
633            .keys()
634            .filter(|k| k.ends_with("_fired") || k.ends_with("_executed"))
635            .cloned()
636            .collect();
637        for key in keys_to_remove {
638            self.facts.remove(&key);
639        }
640    }
641}
642
643impl Default for TypedReteUlEngine {
644    fn default() -> Self {
645        Self::new()
646    }
647}
648