1use std::collections::HashMap;
4use std::sync::atomic::{AtomicU64, Ordering};
5use std::sync::{Arc, Mutex};
6
7use tokio::sync::mpsc;
8use tokio_tungstenite::tungstenite::Message;
9
10type ClientId = u64;
11
12type Sender = mpsc::UnboundedSender<Message>;
13type RoomClients = HashMap<ClientId, Sender>;
14type Rooms = HashMap<String, RoomClients>;
15
16#[derive(Clone)]
17struct HubInner {
18 rooms: Arc<Mutex<Rooms>>,
19 next_id: Arc<AtomicU64>,
20}
21
22#[derive(Clone)]
24pub struct Hub {
25 inner: HubInner,
26}
27
28impl Hub {
29 pub(crate) fn new() -> Self {
30 Self {
31 inner: HubInner {
32 rooms: Arc::new(Mutex::new(HashMap::new())),
33 next_id: Arc::new(AtomicU64::new(1)),
34 },
35 }
36 }
37
38 pub(crate) fn register(
39 &self,
40 room: &str,
41 tx: mpsc::UnboundedSender<Message>,
42 ) -> (ClientId, RoomHandle) {
43 let id = self.inner.next_id.fetch_add(1, Ordering::Relaxed);
44 self.inner
45 .rooms
46 .lock()
47 .unwrap()
48 .entry(room.to_string())
49 .or_default()
50 .insert(id, tx);
51 (
52 id,
53 RoomHandle {
54 inner: Arc::new(RoomHandleInner {
55 hub: self.inner.clone(),
56 room: room.to_string(),
57 id,
58 }),
59 },
60 )
61 }
62
63 fn unregister(&self, room: &str, id: ClientId) {
64 let mut rooms = self.inner.rooms.lock().unwrap();
65 if let Some(clients) = rooms.get_mut(room) {
66 clients.remove(&id);
67 if clients.is_empty() {
68 rooms.remove(room);
69 }
70 }
71 }
72
73 pub async fn broadcast(&self, room: &str, msg: Message) {
75 let senders: Vec<mpsc::UnboundedSender<Message>> = {
76 let rooms = self.inner.rooms.lock().unwrap();
77 rooms
78 .get(room)
79 .map(|clients| clients.values().cloned().collect())
80 .unwrap_or_default()
81 };
82 for tx in senders {
83 let _ = tx.send(msg.clone());
84 }
85 }
86}
87
88struct RoomHandleInner {
89 hub: HubInner,
90 room: String,
91 id: ClientId,
92}
93
94#[derive(Clone)]
96pub struct RoomHandle {
97 inner: Arc<RoomHandleInner>,
98}
99
100impl RoomHandle {
101 pub fn room(&self) -> &str {
102 &self.inner.room
103 }
104}
105
106impl Drop for RoomHandleInner {
107 fn drop(&mut self) {
108 Hub {
109 inner: self.hub.clone(),
110 }
111 .unregister(&self.room, self.id);
112 }
113}