use std::cmp::Ordering;
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
#[cfg(doc)]
use crate::transport::Presence;
#[allow(deprecated)]
mod ditto_address {
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DittoAddress {
pub(crate) pubkey: Vec<u8>,
pub(crate) site_id: Option<crate::transport::SiteId>,
}
}
pub use self::ditto_address::DittoAddress;
#[derive(Debug, Clone, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct PresenceGraph {
pub local_peer: Peer,
pub remote_peers: Vec<Peer>,
}
#[derive(Clone, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct Peer {
pub address: DittoAddress,
#[doc(hidden)]
#[deprecated(note = "use `.peer_key_string` instead")]
pub peer_key: Vec<u8>,
pub peer_key_string: String,
pub device_name: String,
pub os: Option<PresenceOs>,
#[doc(hidden)]
#[deprecated(
note = "This feature has been removed, this value will always be 0 from SDK release 4.8.0"
)]
#[serde(default, skip_deserializing)]
pub query_overlap_group: u8,
pub is_connected_to_ditto_cloud: bool,
pub is_compatible: Option<bool>,
pub ditto_sdk_version: Option<String>,
pub connections: Vec<Connection>,
pub peer_metadata: JsonValue,
pub identity_service_metadata: JsonValue,
}
impl ::core::fmt::Debug for Peer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Peer")
.field("address", &self.address)
.field("peer_key_string", &self.peer_key_string)
.field("device_name", &self.device_name)
.field("os", &self.os)
.field(
"is_connected_to_ditto_cloud",
&self.is_connected_to_ditto_cloud,
)
.field("is_compatible", &self.is_compatible)
.field("ditto_sdk_version", &self.ditto_sdk_version)
.field("connections", &self.connections)
.field("peer_metadata", &self.peer_metadata)
.field("identity_service_metadata", &self.identity_service_metadata)
.finish_non_exhaustive()
}
}
#[doc(hidden)]
#[allow(unused_imports)]
#[deprecated(note = "Use `Connection` instead")]
pub use Connection as UndirectedConnection;
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct Connection {
pub id: String,
#[doc(hidden)]
#[deprecated(note = "use `.peer_key_string1` instead")]
pub peer1: Vec<u8>,
#[doc(hidden)]
#[deprecated(note = "use `.peer_key_string2` instead")]
pub peer2: Vec<u8>,
pub peer_key_string1: String,
pub peer_key_string2: String,
pub connection_type: super::ConnectionType,
pub approximate_distance_in_meters: Option<f32>,
}
impl ::core::fmt::Debug for Connection {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Connection")
.field("id", &self.id)
.field("peer_key_string1", &self.peer_key_string1)
.field("peer_key_string2", &self.peer_key_string2)
.field("connection_type", &self.connection_type)
.field(
"approximate_distance_in_meters",
&self.approximate_distance_in_meters,
)
.finish_non_exhaustive()
}
}
impl Eq for Connection {}
impl PartialEq<Self> for Connection {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl PartialOrd for Connection {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Connection {
fn cmp(&self, other: &Self) -> Ordering {
self.id.cmp(&other.id)
}
}
#[derive(PartialEq, Eq, Hash, Clone, Debug, Serialize, Deserialize)]
pub enum PresenceOs {
#[serde(rename = "Generic")]
Generic,
#[serde(rename = "iOS")]
Ios,
#[serde(rename = "Android")]
Android,
#[serde(rename = "Linux")]
Linux,
#[serde(rename = "Windows")]
Windows,
#[serde(rename = "macOS")]
MacOS,
}
impl std::fmt::Display for PresenceOs {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PresenceOs::Generic => write!(f, "Generic"),
PresenceOs::Ios => write!(f, "iOS"),
PresenceOs::Android => write!(f, "Android"),
PresenceOs::Linux => write!(f, "Linux"),
PresenceOs::MacOS => write!(f, "macOS"),
PresenceOs::Windows => write!(f, "Windows"),
}
}
}
#[allow(deprecated)]
pub use connection_type::ConnectionType;
#[allow(deprecated)]
mod connection_type {
use super::*;
#[doc(hidden)]
#[deprecated(note = "Use `ConnectionType` instead")]
#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Copy, Debug, Serialize, Deserialize)]
pub enum ConnectionType {
Bluetooth,
AccessPoint,
P2PWiFi,
WebSocket,
}
}
#[cfg(test)]
mod tests {
use super::*;
const V3_PEER_JSON: &str = r#"
{
"localPeer": {
"address": {"siteId":1, "pubkey":[1]},
"deviceName": "local-peer",
"peerKey": [1],
"peerKeyString": "pkAQ",
"peerMetadata": {},
"identityServiceMetadata": {},
"os": "macOS",
"isConnectedToDittoCloud": false,
"isCompatible": true,
"dittoSdkVersion": "1.0.0",
"meshRole": 0,
"queryOverlapGroup": 0,
"connections" : [
{
"id": "pkAQ<->pkAg:Bluetooth",
"peer1": [1],
"peer2": [2],
"peerKeyString1": "pkAQ",
"peerKeyString2": "pkAg",
"connectionType": "Bluetooth",
"approximateDistanceInMeters": null
},
{
"id": "pkAQ<->pkAw:AccessPoint",
"peer1": [1],
"peer2": [3],
"peerKeyString1": "pkAQ",
"peerKeyString2": "pkAw",
"connectionType": "AccessPoint",
"approximateDistanceInMeters": null
},
{
"id": "pkAQ<->pkBA:WebSocket",
"peer1": [1],
"peer2": [4],
"peerKeyString1": "pkAQ",
"peerKeyString2": "pkBA",
"connectionType": "WebSocket",
"approximateDistanceInMeters": null
}
]
},
"remotePeers": [
{
"address": {"siteId":2, "pubkey":[2]},
"peerKey": [2],
"peerKeyString": "pkAG",
"peerMetadata": {},
"identityServiceMetadata": {},
"deviceName": "device-2",
"os": "iOS",
"isConnectedToDittoCloud": false,
"isCompatible": true,
"dittoSdkVersion": null,
"meshRole": 0,
"queryOverlapGroup": 0,
"connections" : [
{
"id": "pkAQ<->pkAg:Bluetooth",
"peer1": [1],
"peer2": [2],
"peerKeyString1": "pkAQ",
"peerKeyString2": "pkAg",
"connectionType": "Bluetooth",
"approximateDistanceInMeters": null
}
]
},
{
"address": {"siteId":3, "pubkey":[3]},
"peerKey": [3],
"peerKeyString": "pkAw",
"peerMetadata": {},
"identityServiceMetadata": {},
"deviceName": "device-3",
"os": "Android",
"isConnectedToDittoCloud": false,
"isCompatible": true,
"dittoSdkVersion": "1.0.3",
"meshRole": 32,
"queryOverlapGroup": 32,
"connections" : [
{
"id": "pkAQ<->pkAw:AccessPoint",
"peer1": [1],
"peer2": [3],
"peerKeyString1": "pkAQ",
"peerKeyString2": "pkAw",
"connectionType": "AccessPoint",
"approximateDistanceInMeters": null
}
]
},
{
"address": {"siteId":4, "pubkey":[4]},
"peerKey": [4],
"peerKeyString": "pkBA",
"peerMetadata": {},
"identityServiceMetadata": {},
"deviceName": "device-4",
"os": "Linux",
"isConnectedToDittoCloud": false,
"isCompatible": true,
"dittoSdkVersion": null,
"connections" : [
{
"id": "pkAQ<->pkBA:WebSocket",
"peer1": [1],
"peer2": [4],
"peerKeyString1": "pkAQ",
"peerKeyString2": "pkBA",
"connectionType": "WebSocket",
"approximateDistanceInMeters": null
}
]
}
]
}
"#;
#[test]
fn test_json_parsing() {
#![allow(deprecated)]
let graph: PresenceGraph = serde_json::from_str(V3_PEER_JSON).unwrap();
assert_eq!(graph.local_peer.address.pubkey, vec![1]);
assert_eq!(graph.local_peer.device_name, "local-peer");
assert_eq!(graph.local_peer.os, Some(PresenceOs::MacOS));
assert_eq!(graph.local_peer.query_overlap_group, 0);
assert_eq!(graph.local_peer.connections.len(), 3);
assert_eq!(graph.local_peer.peer_key, vec![1]);
assert_eq!(graph.local_peer.peer_key_string, "pkAQ");
assert_eq!(graph.local_peer.connections[0].peer_key_string1, "pkAQ");
assert_eq!(graph.local_peer.connections[0].peer_key_string2, "pkAg");
assert_eq!(graph.local_peer.connections[1].peer_key_string1, "pkAQ");
assert_eq!(graph.local_peer.connections[1].peer_key_string2, "pkAw");
assert_eq!(graph.local_peer.connections[2].peer_key_string1, "pkAQ");
assert_eq!(graph.local_peer.connections[2].peer_key_string2, "pkBA");
assert_eq!(graph.remote_peers[0].address.pubkey, vec![2]);
assert_eq!(graph.remote_peers[0].peer_key, vec![2]);
assert_eq!(graph.remote_peers[0].device_name, "device-2");
assert_eq!(graph.remote_peers[0].os, Some(PresenceOs::Ios));
let connections = &graph.remote_peers[0].connections;
assert_eq!(connections[0].peer_key_string1, "pkAQ");
assert_eq!(connections[0].peer_key_string2, "pkAg");
assert_eq!(graph.remote_peers[1].address.pubkey, vec![3]);
assert_eq!(graph.remote_peers[1].peer_key, vec![3]);
assert_eq!(graph.remote_peers[1].device_name, "device-3");
assert_eq!(graph.remote_peers[1].os, Some(PresenceOs::Android));
let connections = &graph.remote_peers[1].connections;
assert_eq!(connections[0].peer_key_string1, "pkAQ");
assert_eq!(connections[0].peer_key_string2, "pkAw");
assert_eq!(graph.remote_peers[2].address.pubkey, vec![4]);
assert_eq!(graph.remote_peers[2].peer_key, vec![4]);
assert_eq!(graph.remote_peers[2].device_name, "device-4");
assert_eq!(graph.remote_peers[2].os, Some(PresenceOs::Linux));
let connections = &graph.remote_peers[2].connections;
assert_eq!(connections[0].peer_key_string1, "pkAQ");
assert_eq!(connections[0].peer_key_string2, "pkBA");
#[allow(deprecated)]
{
assert_eq!(graph.local_peer.query_overlap_group, 0);
assert_eq!(graph.remote_peers[0].query_overlap_group, 0);
assert_eq!(graph.remote_peers[1].query_overlap_group, 0);
assert_eq!(graph.remote_peers[2].query_overlap_group, 0);
}
}
}