1use serde::Serialize;
8use tokio::sync::broadcast;
9
10use crate::counter::ConnectionCounter;
11use crate::error::WsError;
12
13pub struct BroadcastHub<T>
38where
39 T: Clone + Serialize + Send + Sync + 'static,
40{
41 tx: broadcast::Sender<T>,
42 connection_count: ConnectionCounter,
43}
44
45impl<T> Clone for BroadcastHub<T>
46where
47 T: Clone + Serialize + Send + Sync + 'static,
48{
49 fn clone(&self) -> Self {
50 Self {
51 tx: self.tx.clone(),
52 connection_count: self.connection_count.clone(),
53 }
54 }
55}
56
57impl<T> std::fmt::Debug for BroadcastHub<T>
58where
59 T: Clone + Serialize + Send + Sync + 'static,
60{
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 f.debug_struct("BroadcastHub")
63 .field("capacity", &self.capacity())
64 .field("connection_count", &self.connection_count())
65 .field("receiver_count", &self.receiver_count())
66 .finish()
67 }
68}
69
70impl<T> BroadcastHub<T>
71where
72 T: Clone + Serialize + Send + Sync + 'static,
73{
74 pub fn new(capacity: usize) -> Self {
76 let (tx, _) = broadcast::channel(capacity);
77 Self {
78 tx,
79 connection_count: ConnectionCounter::new(),
80 }
81 }
82
83 pub fn from_config(config: &crate::config::WsConfig) -> Self {
85 Self::new(config.broadcast_capacity)
86 }
87
88 pub fn subscribe(&self) -> broadcast::Receiver<T> {
90 self.tx.subscribe()
91 }
92
93 pub fn broadcast(&self, msg: T) -> Result<usize, WsError> {
104 self.tx.send(msg).map_err(|_| WsError::BroadcastFull)
105 }
106
107 pub fn try_broadcast(&self, msg: T) -> usize {
110 self.tx.send(msg).unwrap_or(0)
111 }
112
113 pub fn connection_count(&self) -> u64 {
115 self.connection_count.get()
116 }
117
118 pub fn increment_connections(&self, max: Option<usize>) -> Result<u64, WsError> {
124 self.connection_count.increment(max)
125 }
126
127 pub fn decrement_connections(&self) -> u64 {
129 self.connection_count.decrement()
130 }
131
132 pub fn receiver_count(&self) -> usize {
134 self.tx.receiver_count()
135 }
136
137 pub fn capacity(&self) -> usize {
139 0
147 }
148
149 pub fn sender(&self) -> &broadcast::Sender<T> {
151 &self.tx
152 }
153}
154
155impl<T> BroadcastHub<T>
157where
158 T: Clone + Serialize + Send + Sync + 'static,
159{
160 pub fn with_capacity(capacity: usize) -> Self {
162 Self::new(capacity)
163 }
164}
165
166#[allow(
170 clippy::unwrap_used,
171 clippy::expect_used,
172 clippy::indexing_slicing,
173 clippy::panic
174)]
175#[cfg(test)]
176mod tests {
177 use super::*;
178 use serde::{Deserialize, Serialize};
179
180 #[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
181 struct TestMsg {
182 id: u32,
183 body: String,
184 }
185
186 #[tokio::test]
187 async fn broadcast_and_subscribe() {
188 let hub = BroadcastHub::<TestMsg>::new(16);
189 let mut rx1 = hub.subscribe();
190 let mut rx2 = hub.subscribe();
191 let msg = TestMsg {
192 id: 1,
193 body: "hi".into(),
194 };
195 let n = hub.broadcast(msg.clone()).unwrap();
196 assert_eq!(n, 2);
197 assert_eq!(rx1.recv().await.unwrap(), msg);
198 assert_eq!(rx2.recv().await.unwrap(), msg);
199 }
200
201 #[tokio::test]
202 async fn broadcast_no_receivers_error() {
203 let hub = BroadcastHub::<String>::new(16);
204 let err = hub.broadcast("hello".to_string()).unwrap_err();
206 assert_eq!(err, WsError::BroadcastFull);
207 assert_eq!(hub.try_broadcast("hello".to_string()), 0);
209 }
210
211 #[test]
212 fn connection_count_atomic() {
213 let hub = BroadcastHub::<String>::new(8);
214 assert_eq!(hub.connection_count(), 0);
215 hub.increment_connections(None).unwrap();
216 assert_eq!(hub.connection_count(), 1);
217 hub.increment_connections(None).unwrap();
218 assert_eq!(hub.connection_count(), 2);
219 hub.decrement_connections();
220 assert_eq!(hub.connection_count(), 1);
221 hub.decrement_connections();
222 hub.decrement_connections(); assert_eq!(hub.connection_count(), 0);
224 }
225
226 #[test]
227 fn max_connections_enforced() {
228 let hub = BroadcastHub::<String>::new(8);
229 hub.increment_connections(Some(2)).unwrap();
230 hub.increment_connections(Some(2)).unwrap();
231 let err = hub.increment_connections(Some(2)).unwrap_err();
232 assert_eq!(err, WsError::TooManyConnections);
233 }
234
235 #[test]
236 fn clone_shares_counter() {
237 let hub = BroadcastHub::<String>::new(8);
238 let hub2 = hub.clone();
239 hub.increment_connections(None).unwrap();
240 assert_eq!(hub2.connection_count(), 1);
241 }
242}