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