1use mold_core::ServerEvent;
11use std::sync::Arc;
12use tokio::sync::broadcast;
13
14const BROADCAST_BUFFER: usize = 64;
18
19pub struct EventBroadcaster {
20 tx: broadcast::Sender<ServerEvent>,
21}
22
23impl EventBroadcaster {
24 pub fn new() -> Arc<Self> {
25 let (tx, _rx) = broadcast::channel(BROADCAST_BUFFER);
26 Arc::new(Self { tx })
27 }
28
29 pub fn publish(&self, event: ServerEvent) {
33 let _ = self.tx.send(event);
34 }
35
36 pub fn subscribe(&self) -> broadcast::Receiver<ServerEvent> {
37 self.tx.subscribe()
38 }
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[tokio::test]
46 async fn publish_reaches_all_subscribers() {
47 let events = EventBroadcaster::new();
48 let mut a = events.subscribe();
49 let mut b = events.subscribe();
50
51 events.publish(ServerEvent::JobEnded { id: "j1".into() });
52
53 for rx in [&mut a, &mut b] {
54 match rx.recv().await.unwrap() {
55 ServerEvent::JobEnded { id } => assert_eq!(id, "j1"),
56 other => panic!("unexpected event: {other:?}"),
57 }
58 }
59 }
60
61 #[tokio::test]
62 async fn publish_without_subscribers_does_not_panic() {
63 let events = EventBroadcaster::new();
64 events.publish(ServerEvent::JobQueued {
65 id: "j1".into(),
66 model: "flux-dev:q4".into(),
67 });
68 let mut rx = events.subscribe();
70 assert!(matches!(
71 rx.try_recv(),
72 Err(broadcast::error::TryRecvError::Empty)
73 ));
74 }
75}