#![allow(deprecated)]
use crate::engine::rule::Rule;
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone)]
pub struct AgendaManager {
active_group: String,
focus_stack: Vec<String>,
activated_groups: HashSet<String>,
fired_rules_per_activation: HashMap<String, HashSet<String>>,
}
impl Default for AgendaManager {
fn default() -> Self {
Self::new()
}
}
impl AgendaManager {
pub fn new() -> Self {
Self {
active_group: "MAIN".to_string(),
focus_stack: vec!["MAIN".to_string()],
activated_groups: HashSet::new(),
fired_rules_per_activation: HashMap::new(),
}
}
pub fn set_focus(&mut self, group: &str) {
let group = group.to_string();
self.focus_stack.retain(|g| g != &group);
self.focus_stack.push(group.clone());
self.active_group = group.clone();
self.activated_groups.insert(group.clone());
self.fired_rules_per_activation
.insert(group, HashSet::new());
}
pub fn get_active_group(&self) -> &str {
&self.active_group
}
pub fn should_evaluate_rule(&self, rule: &Rule) -> bool {
match &rule.agenda_group {
Some(group) => group == &self.active_group,
None => self.active_group == "MAIN", }
}
pub fn can_fire_rule(&self, rule: &Rule) -> bool {
if !rule.lock_on_active {
return true;
}
let main_group = "MAIN".to_string();
let group = rule.agenda_group.as_ref().unwrap_or(&main_group);
if !self.activated_groups.contains(group) {
return true;
}
if let Some(fired_rules) = self.fired_rules_per_activation.get(group) {
!fired_rules.contains(&rule.name)
} else {
true
}
}
pub fn mark_rule_fired(&mut self, rule: &Rule) {
if rule.lock_on_active {
let main_group = "MAIN".to_string();
let group = rule.agenda_group.as_ref().unwrap_or(&main_group);
self.activated_groups.insert(group.clone());
self.fired_rules_per_activation
.entry(group.clone())
.or_default()
.insert(rule.name.clone());
}
}
pub fn pop_focus(&mut self) -> Option<String> {
if self.focus_stack.len() > 1 {
self.focus_stack.pop();
if let Some(previous) = self.focus_stack.last() {
self.active_group = previous.clone();
Some(previous.clone())
} else {
None
}
} else {
None
}
}
pub fn clear_focus(&mut self) {
self.focus_stack.clear();
self.focus_stack.push("MAIN".to_string());
self.active_group = "MAIN".to_string();
}
pub fn get_agenda_groups(&self, rules: &[Rule]) -> Vec<String> {
let mut groups = HashSet::new();
groups.insert("MAIN".to_string());
for rule in rules {
if let Some(group) = &rule.agenda_group {
groups.insert(group.clone());
}
}
groups.into_iter().collect()
}
pub fn filter_rules<'a>(&self, rules: &'a [Rule]) -> Vec<&'a Rule> {
rules
.iter()
.filter(|rule| self.should_evaluate_rule(rule))
.collect()
}
pub fn reset_cycle(&mut self) {
}
}
#[derive(Debug, Clone)]
pub struct ActivationGroupManager {
fired_groups: HashSet<String>,
}
impl Default for ActivationGroupManager {
fn default() -> Self {
Self::new()
}
}
impl ActivationGroupManager {
pub fn new() -> Self {
Self {
fired_groups: HashSet::new(),
}
}
pub fn can_fire(&self, rule: &Rule) -> bool {
if let Some(group) = &rule.activation_group {
!self.fired_groups.contains(group)
} else {
true }
}
pub fn mark_fired(&mut self, rule: &Rule) {
if let Some(group) = &rule.activation_group {
self.fired_groups.insert(group.clone());
}
}
pub fn reset_cycle(&mut self) {
self.fired_groups.clear();
}
pub fn get_activation_groups(&self, rules: &[Rule]) -> Vec<String> {
rules
.iter()
.filter_map(|rule| rule.activation_group.clone())
.collect::<HashSet<_>>()
.into_iter()
.collect()
}
pub fn has_group_fired(&self, group: &str) -> bool {
self.fired_groups.contains(group)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::engine::rule::{Condition, ConditionGroup, Rule};
use crate::types::{Operator, Value};
fn create_dummy_condition() -> ConditionGroup {
let condition = Condition {
expression: crate::engine::rule::ConditionExpression::Field("test".to_string()),
field: "test".to_string(),
operator: Operator::Equal,
value: Value::Boolean(true),
};
ConditionGroup::single(condition)
}
#[test]
fn test_agenda_manager_basic() {
let mut manager = AgendaManager::new();
assert_eq!(manager.get_active_group(), "MAIN");
manager.set_focus("validation");
assert_eq!(manager.get_active_group(), "validation");
manager.set_focus("processing");
assert_eq!(manager.get_active_group(), "processing");
manager.pop_focus();
assert_eq!(manager.get_active_group(), "validation");
}
#[test]
fn test_agenda_manager_rule_filtering() {
let mut manager = AgendaManager::new();
let rule1 = Rule::new("Rule1".to_string(), create_dummy_condition(), vec![])
.with_agenda_group("validation".to_string());
let rule2 = Rule::new("Rule2".to_string(), create_dummy_condition(), vec![]);
let rules = vec![rule1.clone(), rule2.clone()];
let filtered = manager.filter_rules(&rules);
assert_eq!(filtered.len(), 1);
assert_eq!(filtered[0].name, "Rule2");
manager.set_focus("validation");
let filtered = manager.filter_rules(&rules);
assert_eq!(filtered.len(), 1);
assert_eq!(filtered[0].name, "Rule1");
}
#[test]
fn test_activation_group_manager() {
let mut manager = ActivationGroupManager::new();
let rule1 = Rule::new("Rule1".to_string(), create_dummy_condition(), vec![])
.with_activation_group("discount".to_string());
let rule2 = Rule::new("Rule2".to_string(), create_dummy_condition(), vec![])
.with_activation_group("discount".to_string());
assert!(manager.can_fire(&rule1));
assert!(manager.can_fire(&rule2));
manager.mark_fired(&rule1);
assert!(!manager.can_fire(&rule2));
assert!(manager.has_group_fired("discount"));
manager.reset_cycle();
assert!(manager.can_fire(&rule1));
assert!(manager.can_fire(&rule2));
}
#[test]
fn test_lock_on_active() {
let mut manager = AgendaManager::new();
let rule = Rule::new("TestRule".to_string(), create_dummy_condition(), vec![])
.with_lock_on_active(true);
assert!(manager.can_fire_rule(&rule));
manager.mark_rule_fired(&rule);
assert!(!manager.can_fire_rule(&rule));
manager.reset_cycle();
assert!(!manager.can_fire_rule(&rule));
manager.set_focus("validation");
manager.set_focus("MAIN");
assert!(manager.can_fire_rule(&rule));
}
}