1use std::fmt;
2use std::pin::Pin;
3use std::sync::{Arc, Mutex, PoisonError, Weak};
4
5use serde::Serialize;
6use serde_json::{Map, Value};
7use tokio::sync::broadcast::error::RecvError;
8use tokio::sync::{broadcast, mpsc, oneshot, watch};
9
10use crate::client;
11use crate::error::Error;
12use crate::identifier::Identifier;
13use crate::message::Message;
14use crate::protocol::Command;
15
16const EVENT_BUFFER: usize = 16;
19
20#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum Event {
24 Connected {
28 reconnected: bool,
30 },
31 Disconnected {
34 will_reconnect: bool,
36 },
37 Rejected,
41}
42
43#[derive(Clone)]
54pub struct Subscription {
55 inner: Arc<Inner>,
56}
57
58struct Inner {
59 client: Weak<client::Inner>,
60 registration: Arc<Registration>,
61 messages: tokio::sync::Mutex<mpsc::Receiver<Message>>,
62 events: tokio::sync::Mutex<broadcast::Receiver<Event>>,
63 callbacks: Option<Arc<watch::Sender<bool>>>,
66}
67
68impl Subscription {
69 pub(crate) fn new(
70 client: Weak<client::Inner>,
71 registration: Arc<Registration>,
72 receivers: Receivers,
73 callbacks: Option<Arc<watch::Sender<bool>>>,
74 ) -> Subscription {
75 Subscription {
76 inner: Arc::new(Inner {
77 client,
78 registration,
79 messages: tokio::sync::Mutex::new(receivers.messages),
80 events: tokio::sync::Mutex::new(receivers.events),
81 callbacks,
82 }),
83 }
84 }
85
86 pub fn key(&self) -> &str {
89 &self.inner.registration.key
90 }
91
92 pub async fn next(&self) -> Option<Message> {
103 let message = self.inner.messages.lock().await.recv().await;
104
105 if message.is_none()
106 && let Some(callbacks) = &self.inner.callbacks
107 {
108 let mut finished = callbacks.subscribe();
109 let _last = finished.wait_for(|finished| *finished).await;
110 }
111 message
112 }
113
114 pub fn err(&self) -> Option<Error> {
117 self.inner.registration.reason()
118 }
119
120 pub async fn next_event(&self) -> Option<Event> {
128 let mut events = self.inner.events.lock().await;
129 loop {
130 match events.recv().await {
131 Ok(event) => break Some(event),
132 Err(RecvError::Lagged(skipped)) => {
133 tracing::warn!(key = %self.key(), skipped, "connection events went unread and were dropped");
134 }
135 Err(RecvError::Closed) => break None,
136 }
137 }
138 }
139
140 pub async fn perform(&self, action: &str, data: impl Serialize) -> Result<(), Error> {
145 let payload = perform_payload(action, data)?;
146 self.client()?
147 .send(Command::message(self.key(), payload))
148 .await
149 }
150
151 pub async fn send(&self, data: impl Serialize) -> Result<(), Error> {
154 let payload = serde_json::to_string(&data)?;
155 self.client()?
156 .send(Command::message(self.key(), payload))
157 .await
158 }
159
160 pub async fn unsubscribe(&self) -> Result<(), Error> {
164 let client = self.client()?;
165 if client.forget(&self.inner.registration, &Error::Unsubscribed) {
166 client.send(Command::unsubscribe(self.key())).await
167 } else {
168 Ok(())
169 }
170 }
171
172 fn client(&self) -> Result<Arc<client::Inner>, Error> {
173 self.inner.client.upgrade().ok_or(Error::Closed)
174 }
175}
176
177impl fmt::Debug for Subscription {
178 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179 f.debug_struct("Subscription")
180 .field("key", &self.key())
181 .finish()
182 }
183}
184
185impl Drop for Inner {
186 fn drop(&mut self) {
187 if let Some(client) = self.client.upgrade()
188 && client.forget(&self.registration, &Error::Unsubscribed)
189 {
190 client.unsubscribe_later(&self.registration.key);
191 }
192 }
193}
194
195fn perform_payload(action: &str, data: impl Serialize) -> Result<String, Error> {
196 let mut fields = match serde_json::to_value(data)? {
197 Value::Object(fields) => Ok(fields),
198 Value::Null => Ok(Map::new()),
199 _ => Err(Error::DataNotAnObject {
200 action: action.to_string(),
201 }),
202 }?;
203 fields.insert("action".to_string(), Value::String(action.to_string()));
204 Ok(serde_json::to_string(&fields)?)
205}
206
207pub struct SubscribeRequest {
243 identifier: Identifier,
244 callbacks: Callbacks,
245}
246
247impl SubscribeRequest {
248 pub fn new(identifier: Identifier) -> SubscribeRequest {
250 SubscribeRequest {
251 identifier,
252 callbacks: Callbacks::default(),
253 }
254 }
255
256 pub fn on_connected<Fut>(
259 mut self,
260 callback: impl Fn(bool) -> Fut + Send + Sync + 'static,
261 ) -> SubscribeRequest
262 where
263 Fut: Future<Output = ()> + Send + 'static,
264 {
265 self.callbacks.connected =
266 Some(Box::new(move |reconnected| Box::pin(callback(reconnected))));
267 self
268 }
269
270 pub fn on_disconnected<Fut>(
272 mut self,
273 callback: impl Fn(bool) -> Fut + Send + Sync + 'static,
274 ) -> SubscribeRequest
275 where
276 Fut: Future<Output = ()> + Send + 'static,
277 {
278 self.callbacks.disconnected = Some(Box::new(move |will_reconnect| {
279 Box::pin(callback(will_reconnect))
280 }));
281 self
282 }
283
284 pub fn on_rejected<Fut>(
286 mut self,
287 callback: impl Fn() -> Fut + Send + Sync + 'static,
288 ) -> SubscribeRequest
289 where
290 Fut: Future<Output = ()> + Send + 'static,
291 {
292 self.callbacks.rejected = Some(Box::new(move |()| Box::pin(callback())));
293 self
294 }
295
296 pub(crate) fn into_parts(self) -> (Identifier, Callbacks) {
297 (self.identifier, self.callbacks)
298 }
299}
300
301impl From<Identifier> for SubscribeRequest {
302 fn from(identifier: Identifier) -> SubscribeRequest {
303 SubscribeRequest::new(identifier)
304 }
305}
306
307type Callback<Arg> = Box<dyn Fn(Arg) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
308
309#[derive(Default)]
310pub(crate) struct Callbacks {
311 connected: Option<Callback<bool>>,
312 disconnected: Option<Callback<bool>>,
313 rejected: Option<Callback<()>>,
314}
315
316impl Callbacks {
317 fn is_empty(&self) -> bool {
318 self.connected.is_none() && self.disconnected.is_none() && self.rejected.is_none()
319 }
320
321 pub(crate) fn spawn(
324 self,
325 events: broadcast::Receiver<Event>,
326 ) -> Option<Arc<watch::Sender<bool>>> {
327 if self.is_empty() {
328 return None;
329 }
330
331 let finished = Arc::new(watch::Sender::new(false));
332 let last = Arc::clone(&finished);
333 tokio::spawn(async move {
334 self.run(events).await;
335 last.send_replace(true);
336 });
337
338 Some(finished)
339 }
340
341 async fn run(self, mut events: broadcast::Receiver<Event>) {
344 loop {
345 match events.recv().await {
346 Ok(Event::Connected { reconnected }) => {
347 if let Some(callback) = &self.connected {
348 callback(reconnected).await;
349 }
350 }
351 Ok(Event::Disconnected { will_reconnect }) => {
352 if let Some(callback) = &self.disconnected {
353 callback(will_reconnect).await;
354 }
355 }
356 Ok(Event::Rejected) => {
357 if let Some(callback) = &self.rejected {
358 callback(()).await;
359 }
360 }
361 Err(RecvError::Lagged(skipped)) => {
362 tracing::warn!(
363 skipped,
364 "a callback fell behind and missed connection events"
365 );
366 }
367 Err(RecvError::Closed) => break,
368 }
369 }
370 }
371}
372
373#[derive(Debug, Clone, Copy, PartialEq, Eq)]
375pub(crate) enum Decision {
376 Confirmed,
377 Rejected,
378}
379
380pub(crate) struct Registration {
384 pub(crate) key: String,
385 decision: Mutex<Option<oneshot::Sender<Decision>>>,
386 messages: Mutex<Option<mpsc::Sender<Message>>>,
387 events: Mutex<Option<broadcast::Sender<Event>>>,
390 reason: Mutex<Option<Error>>,
392}
393
394pub(crate) struct Receivers {
396 pub(crate) decision: oneshot::Receiver<Decision>,
397 pub(crate) messages: mpsc::Receiver<Message>,
398 pub(crate) events: broadcast::Receiver<Event>,
399}
400
401impl Registration {
402 pub(crate) fn register(key: String, buffer: usize) -> (Arc<Registration>, Receivers) {
403 let (decide, decision) = oneshot::channel();
404 let (deliver, messages) = mpsc::channel(buffer.max(1));
405 let (announce, events) = broadcast::channel(EVENT_BUFFER);
406 let registration = Arc::new(Registration {
407 key,
408 decision: Mutex::new(Some(decide)),
409 messages: Mutex::new(Some(deliver)),
410 events: Mutex::new(Some(announce)),
411 reason: Mutex::new(None),
412 });
413 (
414 registration,
415 Receivers {
416 decision,
417 messages,
418 events,
419 },
420 )
421 }
422
423 pub(crate) fn watch_events(&self) -> broadcast::Receiver<Event> {
426 match self
427 .events
428 .lock()
429 .unwrap_or_else(PoisonError::into_inner)
430 .as_ref()
431 {
432 Some(announce) => announce.subscribe(),
433 None => broadcast::channel(1).1,
434 }
435 }
436
437 pub(crate) fn confirm(&self, reconnected: bool) {
441 self.announce(Event::Connected { reconnected });
442 self.decide(Decision::Confirmed);
443 }
444
445 pub(crate) fn reject(&self) {
446 self.announce(Event::Rejected);
447 self.decide(Decision::Rejected);
448 self.close(&Error::Rejected {
449 key: self.key.clone(),
450 });
451 }
452
453 pub(crate) fn disconnect(&self, will_reconnect: bool) {
454 self.announce(Event::Disconnected { will_reconnect });
455 }
456
457 pub(crate) fn deliver(&self, message: Message) -> bool {
460 match self
461 .messages
462 .lock()
463 .unwrap_or_else(PoisonError::into_inner)
464 .as_ref()
465 {
466 Some(deliver) => deliver.try_send(message).is_ok(),
467 None => true,
468 }
469 }
470
471 pub(crate) fn close(&self, reason: &Error) {
474 self.reason
475 .lock()
476 .unwrap_or_else(PoisonError::into_inner)
477 .get_or_insert_with(|| reason.clone());
478 self.messages
479 .lock()
480 .unwrap_or_else(PoisonError::into_inner)
481 .take();
482 self.events
483 .lock()
484 .unwrap_or_else(PoisonError::into_inner)
485 .take();
486 }
487
488 fn reason(&self) -> Option<Error> {
490 self.reason
491 .lock()
492 .unwrap_or_else(PoisonError::into_inner)
493 .clone()
494 }
495
496 fn decide(&self, decision: Decision) {
497 if let Some(decide) = self
498 .decision
499 .lock()
500 .unwrap_or_else(PoisonError::into_inner)
501 .take()
502 && decide.send(decision).is_err()
503 {
504 tracing::trace!(key = %self.key, "nobody is waiting on the subscribe");
505 }
506 }
507
508 fn announce(&self, event: Event) {
509 if let Some(announce) = self
510 .events
511 .lock()
512 .unwrap_or_else(PoisonError::into_inner)
513 .as_ref()
514 && announce.send(event).is_err()
515 {
516 tracing::trace!(key = %self.key, "nobody is listening for events");
517 }
518 }
519}
520
521#[cfg(test)]
522#[allow(
523 clippy::unwrap_used,
524 clippy::expect_used,
525 reason = "a test that can't have what it asked for has nothing left to assert"
526)]
527mod tests {
528 use std::collections::BTreeMap;
529
530 use serde_json::json;
531
532 use super::*;
533
534 #[test]
535 fn perform_payload_carries_the_action_alongside_the_data() {
536 assert_eq!(
537 r#"{"action":"speak","body":"Hello!"}"#,
538 perform_payload("speak", json!({ "body": "Hello!" })).unwrap()
539 );
540 assert_eq!(
541 r#"{"action":"speak"}"#,
542 perform_payload("speak", ()).unwrap()
543 );
544 assert!(matches!(
545 perform_payload("speak", vec!["nope"]),
546 Err(Error::DataNotAnObject { action }) if action == "speak"
547 ));
548 assert!(matches!(
549 perform_payload("speak", BTreeMap::from([(vec![1u8], 1u8)])),
550 Err(Error::Json(_))
551 ));
552 }
553}