//! [`TokioBroadcastEventBus`] — in-process [`EventBus`] backed by //! [`tokio::sync::broadcast`]. //! //! `publish` fans out to all current subscribers. `subscribe` returns the //! domain's [`EventStream`] (a `Box`): a **blocking** iterator //! that pulls events off the broadcast receiver. The Tauri event relay drives //! it from a dedicated thread / blocking task, so blocking is acceptable and //! keeps the domain port signature (`-> EventStream`) intact without forcing it //! to become async. use domain::events::DomainEvent; use domain::ports::{EventBus, EventStream}; use tokio::sync::broadcast; /// Default capacity of the broadcast ring buffer. const DEFAULT_CAPACITY: usize = 1024; /// An in-process event bus relaying [`DomainEvent`]s to all subscribers via a /// Tokio broadcast channel. #[derive(Clone)] pub struct TokioBroadcastEventBus { sender: broadcast::Sender, } impl TokioBroadcastEventBus { /// Creates a bus with the default buffer capacity. #[must_use] pub fn new() -> Self { Self::with_capacity(DEFAULT_CAPACITY) } /// Creates a bus with an explicit buffer capacity. #[must_use] pub fn with_capacity(capacity: usize) -> Self { let (sender, _rx) = broadcast::channel(capacity); Self { sender } } /// Returns a raw async receiver, useful for relays that prefer to consume /// events on the Tokio runtime rather than via the blocking [`EventStream`]. #[must_use] pub fn raw_receiver(&self) -> broadcast::Receiver { self.sender.subscribe() } } impl Default for TokioBroadcastEventBus { fn default() -> Self { Self::new() } } impl EventBus for TokioBroadcastEventBus { fn publish(&self, event: DomainEvent) { // A send error only means there are currently no subscribers; that is // not an error condition for a fire-and-forget bus. let _ = self.sender.send(event); } fn subscribe(&self) -> EventStream { Box::new(BroadcastIter { rx: self.sender.subscribe(), }) } } /// Blocking iterator adapter over a broadcast receiver. `next()` blocks until an /// event arrives; it ends when the channel is closed, and skips past `Lagged` /// notices (dropping the lagged count and continuing). struct BroadcastIter { rx: broadcast::Receiver, } impl Iterator for BroadcastIter { type Item = DomainEvent; fn next(&mut self) -> Option { loop { match self.rx.blocking_recv() { Ok(event) => return Some(event), Err(broadcast::error::RecvError::Lagged(_)) => continue, Err(broadcast::error::RecvError::Closed) => return None, } } } }