autoconnect_common/
registry.rs

1use std::collections::HashMap;
2
3use futures::channel::mpsc;
4use futures_locks::RwLock;
5use uuid::Uuid;
6
7use autopush_common::errors::{ApcErrorKind, Result};
8use autopush_common::notification::Notification;
9
10use crate::protocol::ServerNotification;
11
12/// A connected Websocket client.
13#[derive(Debug)]
14struct RegisteredClient {
15    /// The user agent's unique ID.
16    pub uaid: Uuid,
17    /// The local ID, used to potentially distinquish multiple UAID connections.
18    pub uid: Uuid,
19    /// The inbound channel for delivery of locally routed Push Notifications
20    pub tx: mpsc::UnboundedSender<ServerNotification>,
21}
22
23/// Contains a mapping of UAID to the associated RegisteredClient.
24#[derive(Default)]
25pub struct ClientRegistry {
26    clients: RwLock<HashMap<Uuid, RegisteredClient>>,
27}
28
29impl ClientRegistry {
30    /// Informs this server that a new `client` has connected
31    ///
32    /// For now just registers internal state by keeping track of the `client`,
33    /// namely its channel to send notifications back.
34    pub async fn connect(
35        &self,
36        uaid: Uuid,
37        uid: Uuid,
38    ) -> mpsc::UnboundedReceiver<ServerNotification> {
39        trace!("ClientRegistry::connect");
40        let (tx, snotif_stream) = mpsc::unbounded();
41        let client = RegisteredClient { uaid, uid, tx };
42        let mut clients = self.clients.write().await;
43        if let Some(client) = clients.insert(client.uaid, client) {
44            // Drop existing connection
45            let result = client.tx.unbounded_send(ServerNotification::Disconnect);
46            if result.is_ok() {
47                debug!("ClientRegistry::connect Ghosting client, new one wants to connect");
48            }
49        }
50        snotif_stream
51    }
52
53    /// A notification has come for the uaid
54    pub async fn notify(&self, uaid: Uuid, notif: Notification) -> Result<()> {
55        trace!("ClientRegistry::notify");
56        let clients = self.clients.read().await;
57        if let Some(client) = clients.get(&uaid) {
58            debug!("ClientRegistry::notify Found a client to deliver a notification to");
59            let result = client
60                .tx
61                .unbounded_send(ServerNotification::Notification(notif));
62            if result.is_ok() {
63                debug!("ClientRegistry::notify Dropped notification in queue");
64                return Ok(());
65            }
66        }
67        Err(ApcErrorKind::GeneralError("User not connected".into()).into())
68    }
69
70    /// A check for notification command has come for the uaid
71    pub async fn check_storage(&self, uaid: Uuid) -> Result<()> {
72        trace!("ClientRegistry::check_storage");
73        let clients = self.clients.read().await;
74        if let Some(client) = clients.get(&uaid) {
75            let result = client.tx.unbounded_send(ServerNotification::CheckStorage);
76            if result.is_ok() {
77                debug!("ClientRegistry::check_storage Told client to check storage");
78                return Ok(());
79            }
80        }
81        Err(ApcErrorKind::GeneralError("User not connected".into()).into())
82    }
83
84    /// The client specified by `uaid` has disconnected.
85    pub async fn disconnect(&self, uaid: &Uuid, uid: &Uuid) -> Result<()> {
86        trace!("ClientRegistry::disconnect");
87        let mut clients = self.clients.write().await;
88        let client_exists = clients.get(uaid).is_some_and(|client| client.uid == *uid);
89        if client_exists {
90            clients.remove(uaid).expect("Couldn't remove client?");
91            return Ok(());
92        }
93        Err(ApcErrorKind::GeneralError("User not connected".into()).into())
94    }
95
96    pub async fn count(&self) -> usize {
97        self.clients.read().await.len()
98    }
99}