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}