use std::collections::HashMap;
use std::sync::Mutex;
use serde_json::Value;
use tokio::sync::mpsc;
use uuid::Uuid;
#[derive(Debug, Clone)]
pub struct SseEvent {
pub event_type: String,
pub data: Value,
}
pub struct SseBroadcaster {
subscribers: Mutex<HashMap<String, HashMap<String, mpsc::Sender<SseEvent>>>>,
}
impl Default for SseBroadcaster {
fn default() -> Self {
Self::new()
}
}
impl SseBroadcaster {
pub fn new() -> Self {
Self {
subscribers: Mutex::new(HashMap::new()),
}
}
pub fn subscribe(&self, app_key: &str) -> (String, mpsc::Receiver<SseEvent>) {
let (tx, rx) = mpsc::channel(100);
let subscriber_id = Uuid::new_v4().to_string();
let mut subs = self.subscribers.lock().expect("broadcaster mutex poisoned");
subs.entry(app_key.to_string())
.or_default()
.insert(subscriber_id.clone(), tx);
(subscriber_id, rx)
}
pub fn unsubscribe(&self, app_key: &str, subscriber_id: &str) {
let mut subs = self.subscribers.lock().expect("broadcaster mutex poisoned");
let Some(app_subs) = subs.get_mut(app_key) else {
return;
};
app_subs.remove(subscriber_id);
if app_subs.is_empty() {
subs.remove(app_key);
}
}
pub fn broadcast(&self, app_key: &str, event: SseEvent) {
let subs = self.subscribers.lock().expect("broadcaster mutex poisoned");
let Some(app_subs) = subs.get(app_key) else {
return;
};
for tx in app_subs.values() {
let _ = tx.try_send(event.clone());
}
}
pub fn close(&self, app_key: &str) {
let mut subs = self.subscribers.lock().expect("broadcaster mutex poisoned");
subs.remove(app_key);
}
pub fn close_all(&self) {
let mut subs = self.subscribers.lock().expect("broadcaster mutex poisoned");
subs.clear();
}
}