soothe_client/appkit/
broadcaster.rs1use std::collections::HashMap;
4use std::sync::Mutex;
5
6use serde_json::Value;
7use tokio::sync::mpsc;
8use uuid::Uuid;
9
10#[derive(Debug, Clone)]
13pub struct SseEvent {
14 pub event_type: String,
16 pub data: Value,
18}
19
20pub struct SseBroadcaster {
25 subscribers: Mutex<HashMap<String, HashMap<String, mpsc::Sender<SseEvent>>>>,
26}
27
28impl Default for SseBroadcaster {
29 fn default() -> Self {
30 Self::new()
31 }
32}
33
34impl SseBroadcaster {
35 pub fn new() -> Self {
37 Self {
38 subscribers: Mutex::new(HashMap::new()),
39 }
40 }
41
42 pub fn subscribe(&self, app_key: &str) -> (String, mpsc::Receiver<SseEvent>) {
47 let (tx, rx) = mpsc::channel(100);
48 let subscriber_id = Uuid::new_v4().to_string();
49
50 let mut subs = self.subscribers.lock().expect("broadcaster mutex poisoned");
51 subs.entry(app_key.to_string())
52 .or_default()
53 .insert(subscriber_id.clone(), tx);
54
55 (subscriber_id, rx)
56 }
57
58 pub fn unsubscribe(&self, app_key: &str, subscriber_id: &str) {
60 let mut subs = self.subscribers.lock().expect("broadcaster mutex poisoned");
61 let Some(app_subs) = subs.get_mut(app_key) else {
62 return;
63 };
64 app_subs.remove(subscriber_id);
65 if app_subs.is_empty() {
66 subs.remove(app_key);
67 }
68 }
69
70 pub fn broadcast(&self, app_key: &str, event: SseEvent) {
75 let subs = self.subscribers.lock().expect("broadcaster mutex poisoned");
76 let Some(app_subs) = subs.get(app_key) else {
77 return;
78 };
79 for tx in app_subs.values() {
80 let _ = tx.try_send(event.clone());
81 }
82 }
83
84 pub fn close(&self, app_key: &str) {
86 let mut subs = self.subscribers.lock().expect("broadcaster mutex poisoned");
87 subs.remove(app_key);
88 }
89
90 pub fn close_all(&self) {
92 let mut subs = self.subscribers.lock().expect("broadcaster mutex poisoned");
93 subs.clear();
94 }
95}