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}