1use 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
31pub 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#[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 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 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 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 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 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}