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