rust_rule_engine/rete/
auto_network.rs

1//! Auto RETE network: Rule struct and converter
2use crate::rete::network::{ReteUlNode, build_rete_ul_from_condition_group, evaluate_rete_ul_node};
3use crate::rete::alpha::AlphaNode;
4
5#[derive(Debug, Clone)]
6pub struct Rule {
7    pub name: String,
8    pub conditions: ConditionGroup,
9    pub action: String,
10}
11
12#[derive(Debug, Clone)]
13pub enum ConditionGroup {
14    Single(Condition),
15    Compound {
16        left: Box<ConditionGroup>,
17        operator: String,
18        right: Box<ConditionGroup>,
19    },
20    Not(Box<ConditionGroup>),
21    Exists(Box<ConditionGroup>),
22    Forall(Box<ConditionGroup>),
23}
24
25
26#[derive(Debug, Clone)]
27pub struct Condition {
28    pub field: String,
29    pub operator: String,
30    pub value: String,
31}
32
33/// Convert Rule to RETE-UL node network (auto)
34pub fn build_rete_ul_from_rule(rule: &Rule) -> ReteUlNode {
35    let cond_node = build_rete_ul_from_condition_group(&rule.conditions);
36    ReteUlNode::UlAnd(Box::new(cond_node), Box::new(ReteUlNode::UlTerminal(rule.name.clone())))
37}
38
39// Đã chuyển sang build_rete_ul_from_condition_group trong network.rs
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44    use std::collections::HashMap;
45    use crate::rete::network::evaluate_rete_ul_node;
46
47    #[test]
48    fn test_auto_rete_conversion() {
49        let rule = Rule {
50            name: "ActiveAdult".to_string(),
51            conditions: ConditionGroup::Compound {
52                left: Box::new(ConditionGroup::Single(Condition {
53                    field: "status".to_string(),
54                    operator: "==".to_string(),
55                    value: "active".to_string(),
56                })),
57                operator: "AND".to_string(),
58                right: Box::new(ConditionGroup::Single(Condition {
59                    field: "age".to_string(),
60                    operator: ">".to_string(),
61                    value: "18".to_string(),
62                })),
63            },
64            action: "notify".to_string(),
65        };
66    let rete_node = build_rete_ul_from_rule(&rule);
67        let mut facts = HashMap::new();
68        facts.insert("status".to_string(), "active".to_string());
69        facts.insert("age".to_string(), "20".to_string());
70    let result = evaluate_rete_ul_node(&rete_node, &facts);
71        assert!(result);
72
73        // Test OR logic
74        let or_group = ConditionGroup::Compound {
75            left: Box::new(ConditionGroup::Single(Condition {
76                field: "user.status".to_string(),
77                operator: "==".to_string(),
78                value: "active".to_string(),
79            })),
80            operator: "OR".to_string(),
81            right: Box::new(ConditionGroup::Single(Condition {
82                field: "user.age".to_string(),
83                operator: ">".to_string(),
84                value: "18".to_string(),
85            })),
86        };
87        let or_rule = Rule {
88            name: "ActiveOrAdult".to_string(),
89            conditions: or_group,
90            action: "notify".to_string(),
91        };
92    let or_node = build_rete_ul_from_rule(&or_rule);
93        let mut facts2 = HashMap::new();
94        facts2.insert("user.status".to_string(), "inactive".to_string());
95        facts2.insert("user.age".to_string(), "20".to_string());
96    let result_or = evaluate_rete_ul_node(&or_node, &facts2);
97        assert!(result_or);
98
99        let mut facts3 = HashMap::new();
100        facts3.insert("user.status".to_string(), "active".to_string());
101        facts3.insert("user.age".to_string(), "15".to_string());
102    let result_or2 = evaluate_rete_ul_node(&or_node, &facts3);
103        assert!(result_or2);
104
105        let mut facts4 = HashMap::new();
106        facts4.insert("user.status".to_string(), "inactive".to_string());
107        facts4.insert("user.age".to_string(), "15".to_string());
108    let result_or3 = evaluate_rete_ul_node(&or_node, &facts4);
109        assert!(!result_or3);
110    }
111
112    #[test]
113    fn test_exists_forall_and_types() {
114        // EXISTS: ít nhất một user.age > 18
115        let exists_group = ConditionGroup::Exists(Box::new(ConditionGroup::Single(Condition {
116            field: "user.age".to_string(),
117            operator: ">".to_string(),
118            value: "18".to_string(),
119        })));
120        let exists_rule = Rule {
121            name: "AnyAdult".to_string(),
122            conditions: exists_group,
123            action: "notify".to_string(),
124        };
125    let exists_node = build_rete_ul_from_rule(&exists_rule);
126        let mut facts = HashMap::new();
127        facts.insert("user1.age".to_string(), "15".to_string());
128        facts.insert("user2.age".to_string(), "22".to_string());
129        facts.insert("user3.age".to_string(), "17".to_string());
130    let result_exists = evaluate_rete_ul_node(&exists_node, &facts);
131        assert!(result_exists);
132
133        // FORALL: tất cả order.amount > 100
134        let forall_group = ConditionGroup::Forall(Box::new(ConditionGroup::Single(Condition {
135            field: "order.amount".to_string(),
136            operator: ">".to_string(),
137            value: "100".to_string(),
138        })));
139        let forall_rule = Rule {
140            name: "AllBigOrder".to_string(),
141            conditions: forall_group,
142            action: "notify".to_string(),
143        };
144    let forall_node = build_rete_ul_from_rule(&forall_rule);
145        let mut facts2 = HashMap::new();
146        facts2.insert("order1.amount".to_string(), "120".to_string());
147        facts2.insert("order2.amount".to_string(), "150".to_string());
148        facts2.insert("order3.amount".to_string(), "101".to_string());
149    let result_forall = evaluate_rete_ul_node(&forall_node, &facts2);
150        assert!(result_forall);
151
152        // FORALL: một order không đủ
153        let mut facts3 = HashMap::new();
154        facts3.insert("order1.amount".to_string(), "120".to_string());
155        facts3.insert("order2.amount".to_string(), "99".to_string());
156        facts3.insert("order3.amount".to_string(), "101".to_string());
157    let result_forall2 = evaluate_rete_ul_node(&forall_node, &facts3);
158        assert!(!result_forall2);
159
160        // Kiểu bool: user.active == true
161        let bool_group = ConditionGroup::Single(Condition {
162            field: "user.active".to_string(),
163            operator: "==".to_string(),
164            value: "true".to_string(),
165        });
166        let bool_rule = Rule {
167            name: "UserActive".to_string(),
168            conditions: bool_group,
169            action: "notify".to_string(),
170        };
171    let bool_node = build_rete_ul_from_rule(&bool_rule);
172        let mut facts4 = HashMap::new();
173        facts4.insert("user.active".to_string(), "true".to_string());
174    let result_bool = evaluate_rete_ul_node(&bool_node, &facts4);
175        assert!(result_bool);
176        facts4.insert("user.active".to_string(), "false".to_string());
177    let result_bool2 = evaluate_rete_ul_node(&bool_node, &facts4);
178        assert!(!result_bool2);
179    }
180}