rust_rule_engine/rete/
auto_network.rs

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