Skip to main content

backbone_messaging/
subscriber.rs

1//! Generic event subscriber — reusable subscription wiring for any entity+event pair.
2//!
3//! Eliminates the need for per-entity subscriber boilerplate.  Modules
4//! register a `GenericEventSubscriber` with one or more `Arc<dyn EventHandler<Event>>`
5//! instances instead of implementing handler wiring from scratch for every entity.
6//!
7//! # Example
8//!
9//! ```rust,ignore
10//! use backbone_messaging::subscriber::GenericEventSubscriber;
11//! use backbone_messaging::crud_event::CrudEvent;
12//!
13//! // Listen to StoredFile CRUD events and update a search index.
14//! let subscriber = GenericEventSubscriber::<CrudEvent<StoredFile>>::new(
15//!     vec!["entity.created", "entity.updated"],
16//!     vec![Arc::new(SearchIndexHandler::new())],
17//! );
18//! ```
19
20use std::future::Future;
21use std::pin::Pin;
22use std::sync::Arc;
23
24use crate::envelope::EventEnvelope;
25use crate::error::EventError;
26use crate::event::DomainEvent;
27use crate::handler::EventHandler;
28
29// ─── SubscriberCallback (kept for SubscriberRegistry convenience) ─────────────
30
31/// Callback type for closure-based event subscribers.
32pub type SubscriberCallback<Event> =
33    Arc<dyn Fn(Event) -> Pin<Box<dyn Future<Output = Result<(), EventError>> + Send>> + Send + Sync>;
34
35// ─── GenericEventSubscriber ───────────────────────────────────────────────────
36
37/// A generic event subscriber backed by a list of `EventHandler` implementations.
38///
39/// `Event` — the concrete event type (must implement `DomainEvent`)
40///
41/// Handlers are called in registration order.  If an event type filter is set,
42/// only events whose `event_type()` matches one of the registered types are dispatched.
43pub struct GenericEventSubscriber<Event: DomainEvent> {
44    /// Event type discriminators this subscriber cares about.
45    /// Empty vec = subscribe to all.
46    subscribed_types: Vec<&'static str>,
47
48    /// The handlers that process dispatched events.
49    handlers: Vec<Arc<dyn EventHandler<Event>>>,
50
51    /// Optional human-readable name for logging.
52    name: &'static str,
53}
54
55impl<Event: DomainEvent + Clone> GenericEventSubscriber<Event> {
56    /// Create a subscriber with explicit type filters and handler list.
57    ///
58    /// `subscribed_types` — filter to only these `event_type()` strings; empty = all.
59    /// `handlers` — ordered list of handlers to invoke.
60    pub fn new(
61        subscribed_types: Vec<&'static str>,
62        handlers: Vec<Arc<dyn EventHandler<Event>>>,
63    ) -> Self {
64        Self {
65            subscribed_types,
66            handlers,
67            name: std::any::type_name::<Event>(),
68        }
69    }
70
71    /// Create a subscriber that receives **all** event types.
72    pub fn all(handlers: Vec<Arc<dyn EventHandler<Event>>>) -> Self {
73        Self::new(vec![], handlers)
74    }
75
76    /// Override the subscriber name (used in logs).
77    pub fn with_name(mut self, name: &'static str) -> Self {
78        self.name = name;
79        self
80    }
81
82    /// Add a handler to the subscriber (builder pattern).
83    pub fn with_handler(mut self, handler: Arc<dyn EventHandler<Event>>) -> Self {
84        self.handlers.push(handler);
85        self
86    }
87
88    /// Returns true if this subscriber should receive the given event type.
89    pub fn is_interested(&self, event_type: &str) -> bool {
90        self.subscribed_types.is_empty() || self.subscribed_types.contains(&event_type)
91    }
92
93    /// Dispatch an event to all registered handlers in order.
94    ///
95    /// The raw event is wrapped in an `EventEnvelope` before dispatch.
96    /// Returns on the first handler error.
97    pub async fn dispatch(&self, event: Event) -> Result<(), EventError> {
98        if !self.is_interested(event.event_type()) {
99            return Ok(());
100        }
101        let envelope = EventEnvelope::new(event);
102        for handler in &self.handlers {
103            handler.handle(envelope.clone()).await?;
104        }
105        Ok(())
106    }
107
108    /// The subscriber's display name.
109    pub fn name(&self) -> &'static str {
110        self.name
111    }
112
113    /// The event types this subscriber handles (empty = all).
114    pub fn subscribed_types(&self) -> &[&'static str] {
115        &self.subscribed_types
116    }
117
118    /// Number of registered handlers.
119    pub fn handler_count(&self) -> usize {
120        self.handlers.len()
121    }
122}
123
124// ─── SubscriberRegistry ───────────────────────────────────────────────────────
125
126/// A registry that holds multiple closure-based subscribers for the same event type.
127///
128/// Dispatch an event to all registered subscribers in registration order.
129///
130/// For `EventHandler`-based subscriptions prefer `GenericEventSubscriber`.
131pub struct SubscriberRegistry<Event: Clone + Send + Sync + 'static> {
132    subscribers: Vec<SubscriberCallback<Event>>,
133}
134
135impl<Event: Clone + Send + Sync + 'static> SubscriberRegistry<Event> {
136    pub fn new() -> Self {
137        Self {
138            subscribers: Vec::new(),
139        }
140    }
141
142    /// Register a closure subscriber.
143    pub fn register<F, Fut>(&mut self, handler: F)
144    where
145        F: Fn(Event) -> Fut + Send + Sync + 'static,
146        Fut: Future<Output = Result<(), EventError>> + Send + 'static,
147    {
148        self.subscribers
149            .push(Arc::new(move |event| Box::pin(handler(event))));
150    }
151
152    /// Dispatch to all registered subscribers, collecting errors.
153    pub async fn dispatch_all(&self, event: Event) -> Vec<EventError> {
154        let mut errors = Vec::new();
155        for subscriber in &self.subscribers {
156            if let Err(e) = subscriber(event.clone()).await {
157                errors.push(e);
158            }
159        }
160        errors
161    }
162
163    pub fn subscriber_count(&self) -> usize {
164        self.subscribers.len()
165    }
166}
167
168impl<Event: Clone + Send + Sync + 'static> Default for SubscriberRegistry<Event> {
169    fn default() -> Self {
170        Self::new()
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use super::*;
177    use crate::event::DomainEvent;
178
179    // A minimal DomainEvent for testing
180    #[derive(Clone, Debug)]
181    struct FakeEvent {
182        event_type: &'static str,
183        id: String,
184    }
185
186    impl DomainEvent for FakeEvent {
187        fn event_type(&self) -> &'static str {
188            self.event_type
189        }
190        fn aggregate_id(&self) -> &str {
191            &self.id
192        }
193    }
194
195    // A simple counting handler for tests
196    struct CountingHandler {
197        count: Arc<tokio::sync::Mutex<u32>>,
198    }
199
200    impl CountingHandler {
201        fn new(count: Arc<tokio::sync::Mutex<u32>>) -> Self {
202            Self { count }
203        }
204    }
205
206    #[async_trait::async_trait]
207    impl EventHandler<FakeEvent> for CountingHandler {
208        async fn handle(&self, _envelope: EventEnvelope<FakeEvent>) -> Result<(), EventError> {
209            *self.count.lock().await += 1;
210            Ok(())
211        }
212        fn event_types(&self) -> Vec<&'static str> {
213            vec![]
214        }
215    }
216
217    #[tokio::test]
218    async fn subscriber_fires_for_matching_type() {
219        let count = Arc::new(tokio::sync::Mutex::new(0u32));
220        let handler = Arc::new(CountingHandler::new(count.clone()));
221
222        let subscriber = GenericEventSubscriber::new(
223            vec!["created"],
224            vec![handler as Arc<dyn EventHandler<FakeEvent>>],
225        );
226
227        assert!(subscriber.is_interested("created"));
228        assert!(!subscriber.is_interested("deleted"));
229
230        let event = FakeEvent { event_type: "created", id: "e1".into() };
231        subscriber.dispatch(event).await.unwrap();
232        assert_eq!(*count.lock().await, 1);
233    }
234
235    #[tokio::test]
236    async fn subscriber_skips_non_matching_type() {
237        let count = Arc::new(tokio::sync::Mutex::new(0u32));
238        let handler = Arc::new(CountingHandler::new(count.clone()));
239
240        let subscriber = GenericEventSubscriber::new(
241            vec!["created"],
242            vec![handler as Arc<dyn EventHandler<FakeEvent>>],
243        );
244
245        let event = FakeEvent { event_type: "deleted", id: "e1".into() };
246        subscriber.dispatch(event).await.unwrap();
247        // Not interested in "deleted" → handler NOT called
248        assert_eq!(*count.lock().await, 0);
249    }
250
251    #[tokio::test]
252    async fn registry_dispatches_to_all_subscribers() {
253        let mut registry = SubscriberRegistry::<FakeEvent>::new();
254        let counter = Arc::new(tokio::sync::Mutex::new(0u32));
255
256        for _ in 0..3 {
257            let c = counter.clone();
258            registry.register(move |_event: FakeEvent| {
259                let cc = c.clone();
260                async move {
261                    *cc.lock().await += 1;
262                    Ok(())
263                }
264            });
265        }
266
267        let errors = registry
268            .dispatch_all(FakeEvent { event_type: "created", id: "1".into() })
269            .await;
270        assert!(errors.is_empty());
271        assert_eq!(*counter.lock().await, 3);
272    }
273}