dittolive_ditto/presence/
connection_request_handler.rs

1use_prelude!();
2use std::sync::{Arc, OnceLock};
3
4use super::JsonObject;
5use crate::transport::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    ///
122    /// - See also: [`crate::transport::Peer::peer_key_string`] for more information on peer keys.
123    pub fn peer_key_string(&self) -> Arc<str> {
124        self.cached_peer_key
125            .get_or_init(|| {
126                ffi_sdk::dittoffi_connection_request_peer_key_string(&*self.raw)
127                    .to_str()
128                    .into()
129            })
130            .retain()
131    }
132
133    /// The network transport of this connection request.
134    ///
135    /// Expect to receive separate connection requests for each network
136    /// transport that connects the local and remote peer.
137    pub fn connection_type(&self) -> ConnectionType {
138        ConnectionType::from_ffi(::ffi_sdk::dittoffi_connection_request_connection_type(
139            &*self.raw,
140        ))
141    }
142}
143
144#[derive(Debug)]
145/// Private, to future-proof the API (_e.g._, imagine `Deny { reason: … }` or whatnot).
146pub(crate) enum ConnectionRequestAuthorizationInner {
147    Allow { _future_fields: () },
148    Deny { _future_fields: () },
149}
150
151/// Indicates whether a connection request should be authorized.
152#[derive(Debug)]
153pub struct ConnectionRequestAuthorization(pub(crate) ConnectionRequestAuthorizationInner);
154
155impl ConnectionRequestAuthorization {
156    #![allow(non_upper_case_globals)]
157
158    /// The connection request will be allowed.
159    pub const Allow: Self = Self(ConnectionRequestAuthorizationInner::Allow { _future_fields: () });
160
161    /// The connection request will be denied.
162    pub const Deny: Self = Self(ConnectionRequestAuthorizationInner::Deny { _future_fields: () });
163
164    pub(crate) fn into_ffi(self, raw: &ffi_sdk::FfiConnectionRequest) {
165        let ffi_authorization = match self.0 {
166            ConnectionRequestAuthorizationInner::Allow { _future_fields: () } => {
167                ffi_sdk::ConnectionRequestAuthorization::Allow
168            }
169            ConnectionRequestAuthorizationInner::Deny { _future_fields: () } => {
170                ffi_sdk::ConnectionRequestAuthorization::Deny
171            }
172        };
173        ffi_sdk::dittoffi_connection_request_authorize(raw, ffi_authorization);
174    }
175}