use crate::{
collections::*,
error::MatcherError,
event::Event,
predicate::ElementaryPredicate,
subscription::{Subscription, SubscriptionKey},
};
#[derive(Debug, Clone)]
pub struct TreeNode<T: SubscriptionKey> {
pub predicate: Option<ElementaryPredicate>,
pub true_children: Vec<usize>,
pub false_children: Vec<usize>,
pub subscriptions: HashSet<T>,
}
impl<T: SubscriptionKey> TreeNode<T> {
pub fn new(predicate: Option<ElementaryPredicate>) -> Self {
Self {
predicate,
true_children: Vec::new(),
false_children: Vec::new(),
subscriptions: HashSet::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct MatchingTree<T: SubscriptionKey> {
pub nodes: Vec<TreeNode<T>>,
pub subscriptions: HashMap<T, Subscription<T>>,
pub root: usize,
}
impl<T: SubscriptionKey> MatchingTree<T> {
pub fn new() -> Self {
let root_node = TreeNode::new(None);
Self {
nodes: vec![root_node],
subscriptions: HashMap::new(),
root: 0,
}
}
pub fn add_subscription(&mut self, subscription: Subscription<T>) {
let sub_id = subscription.id.clone();
if subscription.predicates.is_empty() {
self.nodes[self.root].subscriptions.insert(sub_id.clone());
self.subscriptions.insert(sub_id, subscription);
return;
}
let mut current_nodes = vec![self.root];
for predicate in &subscription.predicates {
let mut next_nodes = Vec::new();
for &node_idx in ¤t_nodes {
let mut found = false;
for &child_idx in &self.nodes[node_idx].true_children {
if let Some(child_pred) = &self.nodes[child_idx].predicate {
if child_pred == predicate {
next_nodes.push(child_idx);
found = true;
break;
}
}
}
if !found {
for &child_idx in &self.nodes[node_idx].false_children {
if let Some(child_pred) = &self.nodes[child_idx].predicate {
if child_pred == predicate {
next_nodes.push(child_idx);
found = true;
break;
}
}
}
}
if !found {
let new_node = TreeNode::new(Some(predicate.clone()));
let new_idx = self.nodes.len();
self.nodes.push(new_node);
self.nodes[node_idx].true_children.push(new_idx);
next_nodes.push(new_idx);
}
}
current_nodes = next_nodes;
}
for &node_idx in ¤t_nodes {
self.nodes[node_idx].subscriptions.insert(sub_id.clone());
}
self.subscriptions.insert(sub_id, subscription);
}
pub fn remove_subscription(&mut self, subscription_id: &T) -> Result<(), MatcherError> {
if !self.subscriptions.contains_key(subscription_id) {
return Err(MatcherError::Other(format!(
"Subscription {subscription_id:?} not found"
)));
}
for node in &mut self.nodes {
node.subscriptions.remove(subscription_id);
}
self.subscriptions.remove(subscription_id);
Ok(())
}
pub fn match_event(&self, event: &impl Event) -> Result<HashSet<T>, MatcherError> {
let mut matches = HashSet::new();
let mut to_visit = vec![self.root];
while let Some(node_idx) = to_visit.pop() {
let node = &self.nodes[node_idx];
matches.extend(node.subscriptions.iter().cloned());
if node.predicate.is_none() {
to_visit.extend(node.true_children.iter().cloned());
to_visit.extend(node.false_children.iter().cloned());
continue;
}
let predicate = node.predicate.as_ref().unwrap();
match predicate.evaluate(event) {
Ok(true) => {
to_visit.extend(node.true_children.iter().cloned());
}
Ok(false) => {
to_visit.extend(node.false_children.iter().cloned());
}
Err(e) => return Err(e),
}
}
let mut final_matches = HashSet::new();
for sub_id in matches {
if let Some(subscription) = self.subscriptions.get(&sub_id) {
if subscription.matches(event)? {
final_matches.insert(sub_id);
}
}
}
Ok(final_matches)
}
pub fn optimize(&mut self) -> Result<(), MatcherError> {
Ok(())
}
pub fn stats(&self) -> TreeStats {
let mut stats = TreeStats {
node_count: self.nodes.len(),
subscription_count: self.subscriptions.len(),
max_depth: 0,
avg_branching_factor: 0.0,
};
let max_depth = 0;
let mut total_children = 0;
for node in &self.nodes {
let children_count = node.true_children.len() + node.false_children.len();
total_children += children_count;
}
if self.nodes.len() > 1 {
stats.avg_branching_factor = total_children as f64 / (self.nodes.len() - 1) as f64;
}
stats.max_depth = max_depth;
stats
}
}
impl<T: SubscriptionKey> Default for MatchingTree<T> {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct TreeStats {
pub node_count: usize,
pub subscription_count: usize,
pub max_depth: usize,
pub avg_branching_factor: f64,
}