Skip to main content

bb_runtime/framework/
event_source.rs

1//! `EventSource` - registered `event_kind โ†’ ComponentTag`
2//! subscription table consulted by the engine's Phase 3 bus event
3//! routing per ENGINE.md ยง10.7.
4//!
5//! Syscall ops with `event_subscriptions()` declarations register
6//! themselves here at install time; the engine's `poll()` Phase 3
7//! consults `subscribers(event_kind)` to deliver each drained
8//! `NodeEvent` to the matching consumer ops.
9
10use std::collections::HashMap;
11
12use crate::ids::ComponentTag;
13
14/// `event_kind โ†’ Vec<ComponentTag>` subscription table.
15#[derive(Default)]
16pub struct EventSource {
17    subscriptions: HashMap<String, Vec<ComponentTag>>,
18}
19
20impl EventSource {
21    /// Construct a fresh, empty registry.
22    pub fn new() -> Self {
23        Self::default()
24    }
25
26    /// Subscribe `tag` to events of `event_kind`. Idempotent -
27    /// re-subscribing the same tag is a no-op.
28    pub fn subscribe(&mut self, event_kind: &str, tag: ComponentTag) {
29        let entry = self
30            .subscriptions
31            .entry(event_kind.to_string())
32            .or_default();
33        if !entry.contains(&tag) {
34            entry.push(tag);
35        }
36    }
37
38    /// Remove `tag`'s subscription to `event_kind` if present.
39    pub fn unsubscribe(&mut self, event_kind: &str, tag: ComponentTag) {
40        if let Some(entry) = self.subscriptions.get_mut(event_kind) {
41            entry.retain(|t| *t != tag);
42            if entry.is_empty() {
43                self.subscriptions.remove(event_kind);
44            }
45        }
46    }
47
48    /// Subscribers to `event_kind`. Returns an empty slice when no
49    /// component is subscribed.
50    pub fn subscribers(&self, event_kind: &str) -> &[ComponentTag] {
51        self.subscriptions
52            .get(event_kind)
53            .map(|v| v.as_slice())
54            .unwrap_or(&[])
55    }
56
57    /// Number of distinct event kinds with at least one subscriber.
58    pub fn len(&self) -> usize {
59        self.subscriptions.len()
60    }
61
62    /// `true` when no subscriptions are registered.
63    pub fn is_empty(&self) -> bool {
64        self.subscriptions.is_empty()
65    }
66}
67