use std::sync::Arc;
use tokio::sync::broadcast;
use super::event::Event;
#[derive(Clone, Debug)]
pub(crate) struct Bus {
tx: broadcast::Sender<Arc<Event>>,
}
impl Bus {
pub fn new(capacity: usize) -> Self {
let capacity = capacity.max(1);
let (tx, _rx) = broadcast::channel::<Arc<Event>>(capacity);
Self { tx }
}
pub fn publish(&self, ev: Event) {
let _ = self.tx.send(Arc::new(ev));
}
pub fn subscribe(&self) -> broadcast::Receiver<Arc<Event>> {
self.tx.subscribe()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::EventKind;
use tokio::sync::broadcast::error::{RecvError, TryRecvError};
#[tokio::test]
async fn capacity_zero_clamps_to_one() {
let bus = Bus::new(0);
let mut rx = bus.subscribe();
bus.publish(Event::new(EventKind::ShutdownRequested));
let ev = rx.recv().await.unwrap();
assert_eq!(ev.kind, EventKind::ShutdownRequested);
}
#[tokio::test]
async fn every_receiver_observes_each_event() {
let bus = Bus::new(16);
let mut a = bus.subscribe();
let mut b = bus.subscribe();
bus.publish(Event::new(EventKind::TaskStarting));
assert_eq!(a.recv().await.unwrap().kind, EventKind::TaskStarting);
assert_eq!(b.recv().await.unwrap().kind, EventKind::TaskStarting);
}
#[tokio::test]
async fn publish_without_subscribers_is_dropped() {
let bus = Bus::new(16);
bus.publish(Event::new(EventKind::TaskStarting));
let mut rx = bus.subscribe();
assert!(matches!(rx.try_recv(), Err(TryRecvError::Empty)));
}
#[tokio::test]
async fn slow_receiver_observes_lagged_then_resumes() {
let bus = Bus::new(2);
let mut rx = bus.subscribe();
for _ in 0..4 {
bus.publish(Event::new(EventKind::TaskStarting));
}
let err = rx
.recv()
.await
.expect_err("lagged receiver must report the skip");
assert!(
matches!(err, RecvError::Lagged(_)),
"expected Lagged, got {err:?}"
);
assert_eq!(rx.recv().await.unwrap().kind, EventKind::TaskStarting);
}
}