dittolive_ditto/presence/
connection_request_handler.rs

1use_prelude!();
2use std::sync::{Arc, OnceLock};
3
4use super::JsonObject;
5use crate::presence::ConnectionType;
6
7/// Contains information about a remote peer that has requested a connection.
8///
9/// Connection requests and their authorization are scoped to a specific Ditto
10/// peer and connection type.
11///
12/// Information about a peer Ditto is currently trying to connect to. This struct is used by the
13/// [`Presence::set_connection_request_handler()`] callback to decide whether the Ditto client
14/// should connect to a given peer or not.
15pub struct ConnectionRequest {
16    raw: Arc<repr_c::Box<ffi_sdk::FfiConnectionRequest>>,
17    // An optional map of peer-provided information, signed by the peer.
18    cached_peer_metadata: OnceLock<Arc<JsonObject>>,
19    // An optional map of identity service-provided information, signed by the identity
20    // service.
21    cached_identity_service_metadata: OnceLock<Arc<JsonObject>>,
22    // The connecting peer's (pub) key (can be empty for legacy peers).
23    cached_peer_key: OnceLock<Arc<str>>,
24}
25
26impl ConnectionRequest {
27    pub(crate) fn new(raw: repr_c::Box<ffi_sdk::FfiConnectionRequest>) -> Self {
28        Self {
29            raw: Arc::new(raw),
30            cached_peer_metadata: <_>::default(),
31            cached_identity_service_metadata: <_>::default(),
32            cached_peer_key: <_>::default(),
33        }
34    }
35
36    pub(crate) fn raw(&self) -> Arc<repr_c::Box<ffi_sdk::FfiConnectionRequest>> {
37        self.raw.retain()
38    }
39
40    /// JSON-encoded metadata associated with the remote peer.
41    ///
42    /// JSON string representing an empty dictionary if the remote peer has not
43    /// set any metadata.
44    ///
45    /// Set peer metadata for the local peer using
46    /// [`Presence::set_peer_metadata()`] or
47    /// [`Presence::set_peer_metadata_json_str()`].
48    pub fn peer_metadata_json_str(&self) -> String {
49        String::from_utf8(
50            ffi_sdk::dittoffi_connection_request_peer_metadata_json(&*self.raw).to_vec(),
51        )
52        .expect("UTF-8")
53    }
54
55    /// [`DeserializeOwned`] convenience wrapper around [`Self::peer_metadata_json_str()`].
56    pub fn peer_metadata_serde<T: DeserializeOwned>(&self) -> Result<T> {
57        let value = ::serde_json::from_str(&self.peer_metadata_json_str())?;
58        Ok(value)
59    }
60
61    /// Metadata associated with the remote peer.
62    ///
63    /// Empty dictionary if the remote peer has not set any metadata.
64    ///
65    /// Set peer metadata for the local peer using
66    /// [`Presence::set_peer_metadata()`] or
67    /// [`Presence::set_peer_metadata_json_str()`].
68    ///
69    /// Convenience property that wraps [`Self::peer_metadata_json_str()`].
70    pub fn peer_metadata(&self) -> Arc<JsonObject> {
71        self.cached_peer_metadata
72            .get_or_init(|| {
73                Arc::new(
74                    self.peer_metadata_serde()
75                        .expect("incorrect json from `dittoffi`"),
76                )
77            })
78            .retain()
79    }
80
81    /// JSON-encoded metadata for the remote peer that is provided by the
82    /// identity service.
83    ///
84    /// Use an authentication webhook to set this value. See Ditto's online
85    /// documentation for more information on how to configure an authentication
86    /// webhook.
87    pub fn identity_service_metadata_json_str(&self) -> String {
88        String::from_utf8(
89            ffi_sdk::dittoffi_connection_request_identity_service_metadata_json(&*self.raw)
90                .to_vec(),
91        )
92        .expect("UTF-8")
93    }
94
95    /// [`DeserializeOwned`] convenience wrapper around
96    /// [`Self::identity_service_metadata_json_str()`].
97    pub fn identity_service_metadata_serde<T: DeserializeOwned>(&self) -> Result<T> {
98        let value = ::serde_json::from_str(&self.identity_service_metadata_json_str())?;
99        Ok(value)
100    }
101
102    /// Metadata for the remote peer that is provided by the identity service.
103    ///
104    /// Use an authentication webhook to set this value. See Ditto's online
105    /// documentation for more information on how to configure an authentication
106    /// webhook.
107    ///
108    /// Convenience property that wraps [`Self::identity_service_metadata_json_str()`].
109    pub fn identity_service_metadata(&self) -> Arc<JsonObject> {
110        self.cached_identity_service_metadata
111            .get_or_init(|| {
112                Arc::new(
113                    self.identity_service_metadata_serde()
114                        .expect("incorrect json from `dittoffi`"),
115                )
116            })
117            .retain()
118    }
119
120    /// The unique peer key of the remote peer.
121    pub fn peer_key(&self) -> Arc<str> {
122        self.cached_peer_key
123            .get_or_init(|| {
124                ffi_sdk::dittoffi_connection_request_peer_key_string(&*self.raw)
125                    .to_str()
126                    .into()
127            })
128            .retain()
129    }
130
131    /// The network transport of this connection request.
132    ///
133    /// Expect to receive separate connection requests for each network
134    /// transport that connects the local and remote peer.
135    pub fn connection_type(&self) -> ConnectionType {
136        ConnectionType::from_ffi(::ffi_sdk::dittoffi_connection_request_connection_type(
137            &*self.raw,
138        ))
139    }
140}
141
142#[derive(Debug)]
143/// Private, to future-proof the API (_e.g._, imagine `Deny { reason: … }` or whatnot).
144pub(crate) enum ConnectionRequestAuthorizationInner {
145    Allow { _future_fields: () },
146    Deny { _future_fields: () },
147}
148
149/// Indicates whether a connection request should be authorized.
150#[derive(Debug)]
151pub struct ConnectionRequestAuthorization(pub(crate) ConnectionRequestAuthorizationInner);
152
153impl ConnectionRequestAuthorization {
154    #![allow(non_upper_case_globals)]
155
156    /// The connection request will be allowed.
157    pub const Allow: Self = Self(ConnectionRequestAuthorizationInner::Allow { _future_fields: () });
158
159    /// The connection request will be denied.
160    pub const Deny: Self = Self(ConnectionRequestAuthorizationInner::Deny { _future_fields: () });
161
162    pub(crate) fn into_ffi(self, raw: &ffi_sdk::FfiConnectionRequest) {
163        let ffi_authorization = match self.0 {
164            ConnectionRequestAuthorizationInner::Allow { _future_fields: () } => {
165                ffi_sdk::ConnectionRequestAuthorization::Allow
166            }
167            ConnectionRequestAuthorizationInner::Deny { _future_fields: () } => {
168                ffi_sdk::ConnectionRequestAuthorization::Deny
169            }
170        };
171        ffi_sdk::dittoffi_connection_request_authorize(raw, ffi_authorization);
172    }
173}