rust_rule_engine/engine/
agenda.rs1#![allow(deprecated)]
2
3use crate::engine::rule::Rule;
4use std::collections::{HashMap, HashSet};
5
6#[derive(Debug, Clone)]
8pub struct AgendaManager {
9 active_group: String,
11 focus_stack: Vec<String>,
13 activated_groups: HashSet<String>,
15 fired_rules_per_activation: HashMap<String, HashSet<String>>,
17}
18
19impl Default for AgendaManager {
20 fn default() -> Self {
21 Self::new()
22 }
23}
24
25impl AgendaManager {
26 pub fn new() -> Self {
28 Self {
29 active_group: "MAIN".to_string(),
30 focus_stack: vec!["MAIN".to_string()],
31 activated_groups: HashSet::new(),
32 fired_rules_per_activation: HashMap::new(),
33 }
34 }
35
36 pub fn set_focus(&mut self, group: &str) {
38 let group = group.to_string();
39
40 self.focus_stack.retain(|g| g != &group);
42
43 self.focus_stack.push(group.clone());
45 self.active_group = group.clone();
46
47 self.activated_groups.insert(group.clone());
49
50 self.fired_rules_per_activation
52 .insert(group, HashSet::new());
53 }
54
55 pub fn get_active_group(&self) -> &str {
57 &self.active_group
58 }
59
60 pub fn should_evaluate_rule(&self, rule: &Rule) -> bool {
62 match &rule.agenda_group {
63 Some(group) => group == &self.active_group,
64 None => self.active_group == "MAIN", }
66 }
67
68 pub fn can_fire_rule(&self, rule: &Rule) -> bool {
70 if !rule.lock_on_active {
71 return true;
72 }
73
74 let main_group = "MAIN".to_string();
75 let group = rule.agenda_group.as_ref().unwrap_or(&main_group);
76
77 if !self.activated_groups.contains(group) {
79 return true;
80 }
81
82 if let Some(fired_rules) = self.fired_rules_per_activation.get(group) {
84 !fired_rules.contains(&rule.name)
85 } else {
86 true
87 }
88 }
89
90 pub fn mark_rule_fired(&mut self, rule: &Rule) {
92 if rule.lock_on_active {
93 let main_group = "MAIN".to_string();
94 let group = rule.agenda_group.as_ref().unwrap_or(&main_group);
95
96 self.activated_groups.insert(group.clone());
98
99 self.fired_rules_per_activation
101 .entry(group.clone())
102 .or_default()
103 .insert(rule.name.clone());
104 }
105 }
106
107 pub fn pop_focus(&mut self) -> Option<String> {
109 if self.focus_stack.len() > 1 {
110 self.focus_stack.pop();
111 if let Some(previous) = self.focus_stack.last() {
112 self.active_group = previous.clone();
113 Some(previous.clone())
114 } else {
115 None
116 }
117 } else {
118 None
119 }
120 }
121
122 pub fn clear_focus(&mut self) {
124 self.focus_stack.clear();
125 self.focus_stack.push("MAIN".to_string());
126 self.active_group = "MAIN".to_string();
127 }
128
129 pub fn get_agenda_groups(&self, rules: &[Rule]) -> Vec<String> {
131 let mut groups = HashSet::new();
132 groups.insert("MAIN".to_string());
133
134 for rule in rules {
135 if let Some(group) = &rule.agenda_group {
136 groups.insert(group.clone());
137 }
138 }
139
140 groups.into_iter().collect()
141 }
142
143 pub fn filter_rules<'a>(&self, rules: &'a [Rule]) -> Vec<&'a Rule> {
145 rules
146 .iter()
147 .filter(|rule| self.should_evaluate_rule(rule))
148 .collect()
149 }
150
151 pub fn reset_cycle(&mut self) {
153 }
157}
158
159#[derive(Debug, Clone)]
161pub struct ActivationGroupManager {
162 fired_groups: HashSet<String>,
164}
165
166impl Default for ActivationGroupManager {
167 fn default() -> Self {
168 Self::new()
169 }
170}
171
172impl ActivationGroupManager {
173 pub fn new() -> Self {
175 Self {
176 fired_groups: HashSet::new(),
177 }
178 }
179
180 pub fn can_fire(&self, rule: &Rule) -> bool {
182 if let Some(group) = &rule.activation_group {
183 !self.fired_groups.contains(group)
184 } else {
185 true }
187 }
188
189 pub fn mark_fired(&mut self, rule: &Rule) {
191 if let Some(group) = &rule.activation_group {
192 self.fired_groups.insert(group.clone());
193 }
194 }
195
196 pub fn reset_cycle(&mut self) {
198 self.fired_groups.clear();
199 }
200
201 pub fn get_activation_groups(&self, rules: &[Rule]) -> Vec<String> {
203 rules
204 .iter()
205 .filter_map(|rule| rule.activation_group.clone())
206 .collect::<HashSet<_>>()
207 .into_iter()
208 .collect()
209 }
210
211 pub fn has_group_fired(&self, group: &str) -> bool {
213 self.fired_groups.contains(group)
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220 use crate::engine::rule::{Condition, ConditionGroup, Rule};
221 use crate::types::{Operator, Value};
222
223 fn create_dummy_condition() -> ConditionGroup {
224 let condition = Condition {
225 expression: crate::engine::rule::ConditionExpression::Field("test".to_string()),
226 field: "test".to_string(),
227 operator: Operator::Equal,
228 value: Value::Boolean(true),
229 };
230 ConditionGroup::single(condition)
231 }
232
233 #[test]
234 fn test_agenda_manager_basic() {
235 let mut manager = AgendaManager::new();
236 assert_eq!(manager.get_active_group(), "MAIN");
237
238 manager.set_focus("validation");
239 assert_eq!(manager.get_active_group(), "validation");
240
241 manager.set_focus("processing");
242 assert_eq!(manager.get_active_group(), "processing");
243
244 manager.pop_focus();
245 assert_eq!(manager.get_active_group(), "validation");
246 }
247
248 #[test]
249 fn test_agenda_manager_rule_filtering() {
250 let mut manager = AgendaManager::new();
251
252 let rule1 = Rule::new("Rule1".to_string(), create_dummy_condition(), vec![])
253 .with_agenda_group("validation".to_string());
254 let rule2 = Rule::new("Rule2".to_string(), create_dummy_condition(), vec![]);
255
256 let rules = vec![rule1.clone(), rule2.clone()];
258 let filtered = manager.filter_rules(&rules);
259 assert_eq!(filtered.len(), 1);
260 assert_eq!(filtered[0].name, "Rule2");
261
262 manager.set_focus("validation");
264 let filtered = manager.filter_rules(&rules);
265 assert_eq!(filtered.len(), 1);
266 assert_eq!(filtered[0].name, "Rule1");
267 }
268
269 #[test]
270 fn test_activation_group_manager() {
271 let mut manager = ActivationGroupManager::new();
272
273 let rule1 = Rule::new("Rule1".to_string(), create_dummy_condition(), vec![])
274 .with_activation_group("discount".to_string());
275 let rule2 = Rule::new("Rule2".to_string(), create_dummy_condition(), vec![])
276 .with_activation_group("discount".to_string());
277
278 assert!(manager.can_fire(&rule1));
280 assert!(manager.can_fire(&rule2));
281
282 manager.mark_fired(&rule1);
284 assert!(!manager.can_fire(&rule2));
285 assert!(manager.has_group_fired("discount"));
286
287 manager.reset_cycle();
289 assert!(manager.can_fire(&rule1));
290 assert!(manager.can_fire(&rule2));
291 }
292
293 #[test]
294 fn test_lock_on_active() {
295 let mut manager = AgendaManager::new();
296
297 let rule = Rule::new("TestRule".to_string(), create_dummy_condition(), vec![])
298 .with_lock_on_active(true);
299
300 assert!(manager.can_fire_rule(&rule));
302
303 manager.mark_rule_fired(&rule);
305
306 assert!(!manager.can_fire_rule(&rule));
308
309 manager.reset_cycle();
311 assert!(!manager.can_fire_rule(&rule));
312
313 manager.set_focus("validation");
315 manager.set_focus("MAIN");
316 assert!(manager.can_fire_rule(&rule));
317 }
318}