dittolive_ditto/presence/mod.rs
1//! Use [`ditto.presence()`] to gain insight into connected Peers in the Ditto **mesh** via the
2//! [`Presence`] API.
3//!
4//! The [`Presence`] API can be used to inspect the ways in which peers are connected to one
5//! another in the Ditto mesh. Using [`.graph()`] or [`.observe()`], you can see an immediate
6//! view of peer connections or receive callbacks with peer connection updates, respectively.
7//!
8//! The key piece of data in the presence API is the [`PresenceGraph`], which contains a
9//! description of the "local peer" as well as a list of "remote peers". Remote peers are any
10//! peers which can be reached by this local peer in the Ditto mesh, either directly via a
11//! transport on this device, or indirectly via multiple hops through other peers.
12//!
13//! # Example
14//!
15//! Let's take a look at how to request a [`PresenceGraph`] and how to read through it:
16//!
17//! ```
18//! use dittolive_ditto::prelude::*;
19//! # fn main() -> anyhow::Result<()> {
20//! # let (_root, ditto) = dittolive_ditto::doctest_helpers::doctest_ditto();
21//!
22//! // Get an immediate `PresenceGraph` showing current connections
23//! let graph = ditto.presence().graph();
24//! let my_key = &graph.local_peer.peer_key;
25//! let my_connections = &graph.local_peer.connections;
26//!
27//! // Let's find all peers that are directly connected to me, the local peer
28//! let direct_peers = my_connections
29//! .iter()
30//! .map(|connection| {
31//! // Choose the peer in this connection that is not me
32//! if connection.peer1 == *my_key {
33//! &connection.peer2
34//! } else {
35//! &connection.peer1
36//! }
37//! })
38//! .collect::<Vec<_>>();
39//! println!("My direct peers are {direct_peers:?}");
40//!
41//! // Let's look up some details about _all_ the remote peers we can see
42//! let remote_peers_summary = graph
43//! .remote_peers
44//! .iter()
45//! .map(|peer| {
46//! // Peer Key => (Name, OS, Connection Count)
47//! (
48//! &peer.peer_key,
49//! (&peer.device_name, &peer.os, peer.connections.len()),
50//! )
51//! })
52//! .collect::<std::collections::BTreeMap<_, _>>();
53//! println!("A summary of my remote peers looks like this: {remote_peers_summary:#?}");
54//! # Ok(())
55//! # }
56//! ```
57//!
58//! Please note that obtaining a [`PresenceGraph`] via [`.graph()`] only shows a
59//! _snapshot_ of what devices were present when the call was made. In order to watch
60//! changes to device presence, we'll need to use [`.observe()`] to register a
61//! callback and receive updates.
62//!
63//! # Example
64//!
65//! Let's look at how we can use the [`.observe()`] API to register a callback and
66//! receive updates whenever the presence of our Ditto mesh changes. We could say the
67//! presence has changed if any peers have added or removed connections in the mesh.
68//!
69//! ```
70//! # use std::sync::{Arc, Mutex};
71//! use dittolive_ditto::prelude::*;
72//! # fn main() -> anyhow::Result<()> {
73//! # let (_root, ditto) = dittolive_ditto::doctest_helpers::doctest_ditto();
74//!
75//! let mut maybe_prev_graph = Arc::new(Mutex::new(None));
76//! let _presence_observer = ditto.presence().register_observer(move |graph| {
77//! let mut maybe_prev_graph = maybe_prev_graph.lock().unwrap();
78//!
79//! let Some(prev_graph) = &*maybe_prev_graph else {
80//! // First presence update! Print what remote peers we see by their keys
81//! let remote_peers = graph
82//! .remote_peers
83//! .iter()
84//! .map(|peer| &peer.peer_key)
85//! .collect::<Vec<_>>();
86//! println!("Received first presence update! Remote peers: {remote_peers:?}");
87//! *maybe_prev_graph = Some(graph.clone());
88//! return;
89//! };
90//!
91//! // Subsequent presence updates can compare the new graph against the old
92//! let prev_remote_peers = prev_graph
93//! .remote_peers
94//! .iter()
95//! .map(|peer| &peer.peer_key)
96//! .collect::<std::collections::HashSet<_>>();
97//! let latest_remote_peers = graph
98//! .remote_peers
99//! .iter()
100//! .map(|peer| &peer.peer_key)
101//! .collect::<std::collections::HashSet<_>>();
102//!
103//! // Detect if new peers have joined the mesh
104//! let new_peers = latest_remote_peers
105//! .difference(&prev_remote_peers)
106//! .collect::<Vec<_>>();
107//! if !new_peers.is_empty() {
108//! println!("New peers joined the mesh! {new_peers:?}");
109//! }
110//!
111//! // Detect if any peers left the mesh
112//! let lost_peers = prev_remote_peers
113//! .difference(&latest_remote_peers)
114//! .collect::<Vec<_>>();
115//! if !lost_peers.is_empty() {
116//! println!("Peers have left the mesh! {lost_peers:?}");
117//! }
118//!
119//! *maybe_prev_graph = Some(graph.clone());
120//! });
121//! # Ok(())
122//! # }
123//! ```
124//!
125//! [`ditto.presence()`]: crate::ditto::Ditto::presence
126//! [`.graph()`]: Presence::graph
127//! [`.observe()`]: Presence::register_observer
128
129#![doc(alias = "mesh")]
130#![warn(missing_docs)]
131
132use_prelude!();
133
134use std::{
135 future::Future,
136 sync::{Mutex, OnceLock, Weak},
137};
138
139pub use observer::PresenceObserver;
140
141use crate::{debug, ditto::DittoFields};
142
143mod connection_request_handler;
144pub(crate) mod observer;
145
146// These items all "publicly" exist here in `presence`, even if defined elsewhere
147pub use self::connection_request_handler::{ConnectionRequest, ConnectionRequestAuthorization};
148pub use crate::transport::v3::{Connection, Peer, PresenceGraph, PresenceOs};
149
150/// Convenience type for JSON objects seen in [`peer_metadata`][0].
151///
152/// [0]: Presence::peer_metadata
153pub type JsonObject = ::serde_json::Map<String, ::serde_json::Value>;
154
155#[derive(Debug, Default)]
156pub(crate) struct PeerMetadataCache {
157 json_str: String,
158 value: Arc<JsonObject>,
159}
160
161/// The entrypoint to the Presence API, obtained with [`ditto.presence()`].
162///
163/// [See the `presence` module for guide-level docs and examples][0]
164///
165/// [`ditto.presence()`]: crate::prelude::Ditto::presence
166/// [0]: crate::presence
167pub struct Presence {
168 ditto: Weak<DittoFields>,
169 peer_metadata_cache: Mutex<PeerMetadataCache>,
170}
171
172// Primary public API
173impl Presence {
174 pub(crate) fn new(ditto: Weak<DittoFields>) -> Self {
175 Self {
176 ditto,
177 peer_metadata_cache: <_>::default(),
178 }
179 }
180
181 /// Return an immediate view of peer connections as a [`PresenceGraph`].
182 ///
183 /// # Example
184 ///
185 /// Let's take a look at how to request a [`PresenceGraph`] and how to read through it:
186 ///
187 /// ```
188 /// use dittolive_ditto::prelude::*;
189 /// # fn main() -> anyhow::Result<()> {
190 /// # let (_root, ditto) = dittolive_ditto::doctest_helpers::doctest_ditto();
191 ///
192 /// // Get an immediate `PresenceGraph` showing current connections
193 /// let graph = ditto.presence().graph();
194 /// let my_key = &graph.local_peer.peer_key;
195 /// let my_connections = &graph.local_peer.connections;
196 ///
197 /// // Let's find all peers that are directly connected to me, the local peer
198 /// let direct_peers = my_connections
199 /// .iter()
200 /// .map(|connection| {
201 /// // Choose the peer in this connection that is not me
202 /// if connection.peer1 == *my_key {
203 /// &connection.peer2
204 /// } else {
205 /// &connection.peer1
206 /// }
207 /// })
208 /// .collect::<Vec<_>>();
209 /// println!("My direct peers are {direct_peers:?}");
210 ///
211 /// // Let's look up some details about _all_ the remote peers we can see
212 /// let remote_peers_summary = graph
213 /// .remote_peers
214 /// .iter()
215 /// .map(|peer| {
216 /// // Peer Key => (Name, OS, Connection Count)
217 /// (
218 /// &peer.peer_key,
219 /// (&peer.device_name, &peer.os, peer.connections.len()),
220 /// )
221 /// })
222 /// .collect::<std::collections::BTreeMap<_, _>>();
223 /// println!("A summary of my remote peers looks like this: {remote_peers_summary:#?}");
224 /// # Ok(())
225 /// # }
226 /// ```
227 ///
228 /// Please note that obtaining a [`PresenceGraph`] via `.graph()` only shows a
229 /// _snapshot_ of what devices were present when the call was made. In order to watch
230 /// changes to device presence, we'll need to use [`.observe(...)`] to register a
231 /// callback and receive updates.
232 ///
233 /// [`.observe(...)`]: Self::register_observer
234 pub fn graph(&self) -> PresenceGraph {
235 let ditto = self.ditto.upgrade().unwrap();
236 let buffer = ffi_sdk::dittoffi_presence_graph(&ditto.ditto);
237 serde_json::from_slice::<PresenceGraph>(&buffer)
238 .expect("should receive UTF-8 encoded JSON PresenceGraph")
239 }
240
241 /// Receive [`PresenceGraph`] updates when peer connections change in the Ditto mesh.
242 ///
243 /// Use [`observer.cancel()`] to stop receiving updates.
244 ///
245 /// # Example
246 ///
247 /// Let's look at how we can use `.observe()` to register a callback and
248 /// receive updates whenever the presence of our Ditto mesh changes. We could say the
249 /// presence has changed if any peers have added or removed connections in the mesh.
250 ///
251 /// ```
252 /// # use std::sync::{Arc, Mutex};
253 /// use dittolive_ditto::prelude::*;
254 /// # fn main() -> anyhow::Result<()> {
255 /// # let (_root, ditto) = dittolive_ditto::doctest_helpers::doctest_ditto();
256 ///
257 /// let mut maybe_prev_graph = Arc::new(Mutex::new(None));
258 /// let _presence_observer = ditto.presence().register_observer(move |graph| {
259 /// let mut maybe_prev_graph = maybe_prev_graph.lock().unwrap();
260 ///
261 /// let Some(prev_graph) = &*maybe_prev_graph else {
262 /// // First presence update! Print what remote peers we see by their keys
263 /// let remote_peers = graph
264 /// .remote_peers
265 /// .iter()
266 /// .map(|peer| &peer.peer_key)
267 /// .collect::<Vec<_>>();
268 /// println!("Received first presence update! Remote peers: {remote_peers:?}");
269 /// *maybe_prev_graph = Some(graph.clone());
270 /// return;
271 /// };
272 ///
273 /// // Subsequent presence updates can compare the new graph against the old
274 /// let prev_remote_peers = prev_graph
275 /// .remote_peers
276 /// .iter()
277 /// .map(|peer| &peer.peer_key)
278 /// .collect::<std::collections::HashSet<_>>();
279 /// let latest_remote_peers = graph
280 /// .remote_peers
281 /// .iter()
282 /// .map(|peer| &peer.peer_key)
283 /// .collect::<std::collections::HashSet<_>>();
284 ///
285 /// // Detect if new peers have joined the mesh
286 /// let new_peers = latest_remote_peers
287 /// .difference(&prev_remote_peers)
288 /// .collect::<Vec<_>>();
289 /// if !new_peers.is_empty() {
290 /// println!("New peers joined the mesh! {new_peers:?}");
291 /// }
292 ///
293 /// // Detect if any peers left the mesh
294 /// let lost_peers = prev_remote_peers
295 /// .difference(&latest_remote_peers)
296 /// .collect::<Vec<_>>();
297 /// if !lost_peers.is_empty() {
298 /// println!("Peers have left the mesh! {lost_peers:?}");
299 /// }
300 ///
301 /// *maybe_prev_graph = Some(graph.clone());
302 /// });
303 /// # Ok(())
304 /// # }
305 /// ```
306 ///
307 /// If instead you just need to check once to learn about which peers are connected
308 /// _right now_, try using the [`.graph()`] method instead.
309 ///
310 /// [`observer.cancel()`]: crate::presence::PresenceObserver::cancel
311 /// [`.graph()`]: Self::graph
312 pub fn register_observer(
313 self: &Arc<Self>,
314 callback: impl Fn(&PresenceGraph) + Send + Sync + 'static,
315 ) -> Result<PresenceObserver> {
316 let ditto = self
317 .ditto
318 .upgrade()
319 .ok_or(ErrorKind::ReleasedDittoInstance)?;
320
321 let callback = Arc::new(Mutex::new(callback));
322 let callback = move |graph: &PresenceGraph| {
323 let callback = callback.lock().unwrap();
324 callback(graph);
325 };
326
327 PresenceObserver::new(&*ditto.ditto, callback)
328 }
329}
330
331// Peer Metadata & on-connecting API.
332impl Presence {
333 /// Set a dictionary of arbitrary data about this device to be shared with peers in the mesh.
334 ///
335 /// This data is gossiped in the presence collection across the mesh. This can be useful to
336 /// include extra metadata like app versions, capabilities, etc., to help peers decide who to
337 /// interact with.
338 ///
339 /// This peer information is persisted in the SDK, and thus needn't be set every time Ditto
340 /// starts.
341 ///
342 /// # Security (and caveats)
343 /// This peer info will be signed by your peer key to prevent forgery of this info by other
344 /// peers.
345 ///
346 /// However, for compatibility, there is no attestation of the *lack* of peer info -- that
347 /// is, participants in the mesh could maliciously remove peer info. If this is a concern
348 /// for your application, a workaround for this is to have your application require that
349 /// peers have a signed peer info dictionary present.
350 ///
351 /// Similarly, as there is no monotonic version counter or timestamp/expiration of the
352 /// signed peer info, replay attacks (replacing the current info with previously, possibly
353 /// outdated, signed info) are possible without counter-measures. If this is a concern
354 /// for your application, you might consider including a counter or creation timestamp
355 /// to prevent replays, depending on your use-case.
356 ///
357 /// # Performance caveats
358 /// Because this information is included in the presence data that is gossiped among peers,
359 /// the size of this peer info and the frequency it is updated can *drastically* affect
360 /// performance if it is too large.
361 ///
362 /// # Errors
363 /// Because of the performance implications, the serialized info dictionary is currently
364 /// limited to 4KiB.
365 ///
366 /// # Examples
367 /// ```
368 /// use dittolive_ditto::prelude::*;
369 /// use serde_json::json;
370 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
371 /// # let (_root, ditto) = dittolive_ditto::doctest_helpers::doctest_ditto();
372 /// ditto.presence().set_peer_metadata(&json!({
373 /// "app_version": "1.0.0",
374 /// }))?;
375 /// # Ok(())
376 /// # }
377 /// ```
378 pub fn set_peer_metadata(&self, peer_metadata: &impl Serialize) -> Result<(), DittoError> {
379 let payload = ::serde_json::to_string(peer_metadata)?;
380 self.set_peer_metadata_json_str(&payload)
381 }
382
383 /// Set arbitrary metadata formatted as JSON to be associated with the
384 /// current peer.
385 ///
386 /// The metadata must not exceed 4 KB in size. Expects JSON.
387 ///
388 /// - See also: [`Self::set_peer_metadata()`] for details on usage of metadata.
389 pub fn set_peer_metadata_json_str(&self, json: &str) -> Result<(), DittoError> {
390 let ditto = self
391 .ditto
392 .upgrade()
393 .ok_or(ErrorKind::ReleasedDittoInstance)?;
394 ffi_sdk::dittoffi_presence_set_peer_metadata_json_throws(
395 &ditto.ditto,
396 json.as_bytes().into(),
397 )
398 .into_rust_result()?;
399 Ok(())
400 }
401
402 /// Metadata associated with the current peer as JSON-encoded data.
403 ///
404 /// Other peers in the same mesh can access this user-provided dictionary of
405 /// metadata via the presence graph at [`Self::graph()`] and when
406 /// evaluating connection requests using
407 /// [`Self::set_connection_request_handler()`]. Use [`Self::set_peer_metadata()`]
408 /// or [`Self::set_peer_metadata_json_str()`] to set this value.
409 pub fn peer_metadata_json_str(&self) -> String {
410 let ditto = self
411 .ditto
412 .upgrade()
413 .ok_or(ErrorKind::ReleasedDittoInstance)
414 .unwrap();
415
416 String::from_utf8(From::<Box<[u8]>>::from(
417 ffi_sdk::dittoffi_presence_peer_metadata_json(&ditto.ditto).into(),
418 ))
419 .expect("UTF-8")
420 }
421
422 /// [`DeserializeOwned`] convenience around [`Self::peer_metadata_json_str()`].
423 pub fn peer_metadata_serde<T: DeserializeOwned>(&self) -> Result<T> {
424 let value = ::serde_json::from_str(&self.peer_metadata_json_str())?;
425 Ok(value)
426 }
427
428 /// Metadata associated with the current peer.
429 ///
430 /// Other peers in the same mesh can access this user-provided dictionary of
431 /// metadata via the presence graph at [`Self::graph()`] and when
432 /// evaluating connection requests using
433 /// [`Self::set_connection_request_handler()`]. Use [`Self::set_peer_metadata()`]
434 /// or [`Self::set_peer_metadata_json_str()`] to set this value.
435 ///
436 /// This is a convenience property that wraps [`Self::peer_metadata_json_str()`].
437 pub fn peer_metadata(&self) -> Arc<JsonObject> {
438 let json_str = self.peer_metadata_json_str();
439 let mut cache = self
440 .peer_metadata_cache
441 .lock()
442 .unwrap_or_else(|it| it.into_inner());
443 if json_str != cache.json_str {
444 *cache = PeerMetadataCache {
445 value: Arc::new(
446 ::serde_json::from_str(&json_str).expect("incorrect json from `dittoffi`"),
447 ),
448 json_str,
449 };
450 }
451 cache.value.retain()
452 }
453
454 /// Set this handler to control which peers in a Ditto mesh can connect to
455 /// the current peer.
456 ///
457 /// Each peer in a Ditto mesh will attempt to connect to other peers that it
458 /// can reach. By default, the mesh will try and establish connections that
459 /// optimize for the best overall connectivity between peers. However, you
460 /// can set this handler to assert some control over which peers you connect
461 /// to.
462 ///
463 /// If set, this handler is called for every incoming connection request
464 /// from a remote peer and is passed the other peer's `peer_key`,
465 /// `peer_metadata`, and `identity_service_metadata`. The handler can then
466 /// accept or reject the request by returning an according
467 /// [`ConnectionRequestAuthorization`] value. When the connection
468 /// request is rejected, the remote peer may retry the connection request
469 /// after a short delay.
470 ///
471 /// Connection request handlers must reliably respond to requests within a
472 /// short time: **if a handler takes too long to return, the connection
473 /// request will fall back to being denied**. The response –currently— times
474 /// out after 10 seconds, but this exact value may be subject to change in
475 /// future releases.
476 ///
477 /// - Note: the handler is called from a different thread ("background hook").
478 /// - See also: [`Self::peer_metadata()`]
479 ///
480 /// ## Example
481 ///
482 /// ```
483 /// # use dittolive_ditto::prelude::*;
484 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
485 /// # let (_root, ditto) = dittolive_ditto::doctest_helpers::doctest_ditto();
486 /// /// Let's imagine the app we are maintaining has a bug in `1.2.3`:
487 /// const BUGGY_VERSION: &str = "1.2.3";
488 ///
489 /// // We avoid problems in updated versions of our app with these ones by
490 /// // rejecting connections to them, like so:
491 /// ditto
492 /// .presence()
493 /// .set_connection_request_handler(|connection_request: ConnectionRequest| {
494 /// match connection_request
495 /// .peer_metadata()
496 /// .get("app_version")
497 /// .and_then(|it| it.as_str())
498 /// {
499 /// // Reject peers reporting a known buggy version or reporting no
500 /// // version at all.
501 /// Some(BUGGY_VERSION) | None => return ConnectionRequestAuthorization::Deny,
502 /// Some(_non_buggy_version) => { /* no reason to reject here */ }
503 /// }
504 /// // Potentially other checks/reasons to reject…
505 ///
506 /// // Eventually:
507 /// ConnectionRequestAuthorization::Allow
508 /// });
509 ///
510 /// // You can also unset the `connection_request_handler` by setting it to `None`.
511 /// // This uses the default handler, which accepts all requests.
512 /// ditto.presence().set_connection_request_handler(None);
513 /// # Ok(())
514 /// # }
515 /// ```
516 pub fn set_connection_request_handler<
517 F: sealed::IntoOption<
518 impl 'static + Send + Sync + Fn(ConnectionRequest) -> ConnectionRequestAuthorization,
519 >,
520 >(
521 &self,
522 handler_or_none: F,
523 ) {
524 let ditto = self
525 .ditto
526 .upgrade()
527 .ok_or(ErrorKind::ReleasedDittoInstance)
528 .unwrap();
529 let ffi_callback = F::into_option(handler_or_none).map(|callback| {
530 Arc::new(move |raw: repr_c::Box<ffi_sdk::FfiConnectionRequest>| {
531 let connection_request = ConnectionRequest::new(raw);
532 let raw = connection_request.raw();
533 callback(connection_request).into_ffi(&raw);
534 })
535 .into()
536 });
537 ffi_sdk::dittoffi_presence_set_connection_request_handler(&ditto.ditto, ffi_callback)
538 }
539
540 /// Convenience around [`Self::set_connection_request_handler()`] that allows the callback to be
541 /// `async`.
542 ///
543 /// Not responding in time will lead to a handshake timeout, effectively rejecting the peer.
544 pub fn set_connection_request_handler_async<ConnectionRequestAuthorizationFut>(
545 &self,
546 async_callback: impl 'static
547 + Send
548 + Sync
549 + Fn(ConnectionRequest) -> ConnectionRequestAuthorizationFut,
550 ) where
551 ConnectionRequestAuthorizationFut:
552 'static + Send + Future<Output = ConnectionRequestAuthorization>,
553 {
554 use tokio::{runtime, sync::mpsc};
555
556 let ditto = self
557 .ditto
558 .upgrade()
559 .ok_or(ErrorKind::ReleasedDittoInstance)
560 .unwrap();
561
562 let new_mini_runtime = || {
563 // Using an unbounded channel to implement `task::spawn()`, like tokio does.
564 let (tx, mut rx) = mpsc::unbounded_channel();
565 let runtime = runtime::Builder::new_current_thread()
566 .enable_all()
567 .build()
568 .expect("failed to build `async` runtime");
569 ::std::thread::spawn(move || {
570 runtime.block_on(async move {
571 while let Some(task) = rx.recv().await {
572 () = task.await;
573 }
574 })
575 });
576 tx
577 };
578
579 ffi_sdk::dittoffi_presence_set_connection_request_handler(
580 &ditto.ditto,
581 Some(
582 Arc::new(move |raw| {
583 static MINI_RUNTIME: OnceLock<
584 mpsc::UnboundedSender<Pin<Box<dyn Send + Future<Output = ()>>>>,
585 > = OnceLock::new();
586
587 let connection_request = ConnectionRequest::new(raw);
588 let raw = connection_request.raw();
589 let task_to_spawn_detached = {
590 let async_callback_connection_request = async_callback(connection_request);
591 async move {
592 async_callback_connection_request.await.into_ffi(&raw);
593 }
594 };
595 MINI_RUNTIME
596 .get_or_init(new_mini_runtime)
597 .send(Box::pin(task_to_spawn_detached))
598 .expect("dedicated async runtime to be alive");
599 })
600 .into(),
601 ),
602 )
603 }
604}
605
606/// Defines a simplified connection type between peers for reporting presence
607/// info.
608///
609/// These connections indicate P2P connections _only_. A connection to the Big Peer
610/// is recorded by a simple boolean flag on the [`Peer`] type.
611#[non_exhaustive]
612#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
613pub enum ConnectionType {
614 /// Connected to the remote peer via Bluetooth Low-Energy (BLE).
615 Bluetooth,
616
617 /// Connected to the remote peer via LAN, e.g. home or office WiFi.
618 AccessPoint,
619
620 /// Direct WiFi between peers, using AWDL (Apple) or WiFi Aware (Android).
621 P2PWiFi,
622
623 /// Connected to the remote peer via WebSocket, a bidirectional stream over http(s).
624 WebSocket,
625
626 /// Unknown connection type, remote peer is likely a higher version.
627 #[doc(hidden)]
628 Unknown,
629}
630
631impl ConnectionType {
632 pub(crate) fn from_ffi(ffi: ::ffi_sdk::ConnectionType) -> Self {
633 match ffi {
634 ::ffi_sdk::ConnectionType::Bluetooth => Self::Bluetooth,
635 ::ffi_sdk::ConnectionType::AccessPoint => Self::AccessPoint,
636 ::ffi_sdk::ConnectionType::P2PWiFi => Self::P2PWiFi,
637 ::ffi_sdk::ConnectionType::WebSocket => Self::WebSocket,
638 #[allow(unreachable_patterns)]
639 _ => {
640 #[allow(deprecated)]
641 {
642 debug!(connection_type = ?ffi, "got unknown `ConnectionType`");
643 }
644 Self::Unknown
645 }
646 }
647 }
648}
649
650mod sealed {
651 use super::*;
652
653 /// A trait implemented for both `None` and instances of
654 /// `'static + Send + Sync + Fn(ConnectionRequest) -> ConnectionRequestAuthorization`.
655 ///
656 /// Used to keep perfect API parity with that of other SDKs.
657 pub trait IntoOption<
658 F: 'static + Send + Sync + Fn(ConnectionRequest) -> ConnectionRequestAuthorization,
659 >
660 {
661 fn into_option(_: Self) -> Option<F>;
662 }
663
664 impl IntoOption<fn(ConnectionRequest) -> ConnectionRequestAuthorization>
665 for Option<::never_say_never::Never>
666 {
667 fn into_option(_: Self) -> Option<fn(ConnectionRequest) -> ConnectionRequestAuthorization> {
668 None
669 }
670 }
671
672 impl<F> IntoOption<F> for F
673 where
674 F: 'static + Send + Sync + Fn(ConnectionRequest) -> ConnectionRequestAuthorization,
675 {
676 fn into_option(f: Self) -> Option<Self> {
677 Some(f)
678 }
679 }
680}