#[cfg(test)]
mod tests;
use std::collections::HashMap;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::{Arc, Mutex};
use crate::response::Response;
use crate::sse::{SseEvent, SseSender, SseStream};
use crate::websocket::{Message, WebSocketConnection, WebSocketSender};
enum Subscriber {
Sse(SseSender),
WebSocket(WebSocketSender),
Channel(Sender<String>),
}
impl Subscriber {
fn deliver(&self, topic: &str, message: &str) -> Option<bool> {
match self {
Subscriber::Sse(tx) => tx.try_send(SseEvent::data(message).event_type(topic)).ok(),
Subscriber::WebSocket(ws) => (!ws.is_closed() && ws.send_text(message).is_ok()).then_some(true),
Subscriber::Channel(tx) => tx.send(message.to_string()).ok().map(|_| true),
}
}
}
#[derive(Clone, Default)]
pub struct Hub {
topics: Arc<Mutex<HashMap<String, Vec<Subscriber>>>>,
}
impl Hub {
pub fn new() -> Self {
Hub::default()
}
fn add(&self, topic: &str, subscriber: Subscriber) {
let mut topics = self.topics.lock().unwrap_or_else(|e| e.into_inner());
topics.entry(topic.to_string()).or_default().push(subscriber);
}
pub fn publish(&self, topic: &str, message: &str) -> usize {
let subscribers = {
let mut topics = self.topics.lock().unwrap_or_else(|e| e.into_inner());
match topics.get_mut(topic) {
Some(list) => std::mem::take(list),
None => return 0,
}
};
let mut delivered = 0;
let mut live = Vec::with_capacity(subscribers.len());
for subscriber in subscribers {
if let Some(sent) = subscriber.deliver(topic, message) {
delivered += sent as usize;
live.push(subscriber);
}
}
let mut topics = self.topics.lock().unwrap_or_else(|e| e.into_inner());
let list = topics.entry(topic.to_string()).or_default();
live.append(list);
if live.is_empty() {
topics.remove(topic);
} else {
*list = live;
}
delivered
}
pub fn sse(&self, topic: &str) -> Response {
let (tx, response) = SseStream::new().start();
self.add(topic, Subscriber::Sse(tx));
response
}
pub fn subscribe_websocket(&self, topic: &str, sender: WebSocketSender) {
self.add(topic, Subscriber::WebSocket(sender));
}
pub fn serve_websocket<F>(&self, mut ws: WebSocketConnection, topic: &str, on_message: F)
where
F: Fn(&Hub, &str, &str),
{
self.subscribe_websocket(topic, ws.sender());
while let Ok(Some(message)) = ws.recv() {
if let Message::Text(text) = message {
on_message(self, topic, &text);
}
}
let _ = ws.close(1000, "");
}
pub fn subscribe(&self, topic: &str) -> Receiver<String> {
let (tx, rx) = channel();
self.add(topic, Subscriber::Channel(tx));
rx
}
pub fn subscribers(&self, topic: &str) -> usize {
self.topics.lock().unwrap_or_else(|e| e.into_inner()).get(topic).map_or(0, Vec::len)
}
pub fn topics(&self) -> Vec<String> {
let mut topics: Vec<String> = self.topics.lock().unwrap_or_else(|e| e.into_inner()).keys().cloned().collect();
topics.sort();
topics
}
}