1use std::cmp::Ordering;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value as JsonValue;
5
6#[cfg(doc)]
7use crate::transport::Presence;
8
9#[allow(deprecated)]
11mod ditto_address {
12 use serde::{Deserialize, Serialize};
13
14 #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
15 #[serde(rename_all = "camelCase")]
17 pub struct DittoAddress {
18 pub(crate) pubkey: Vec<u8>,
19 pub(crate) site_id: Option<crate::transport::SiteId>,
20 }
21}
22pub use self::ditto_address::DittoAddress;
23
24#[derive(Debug, Clone, Deserialize, PartialEq)]
26#[serde(rename_all = "camelCase")]
27#[non_exhaustive]
28pub struct PresenceGraph {
29 pub local_peer: Peer,
31 pub remote_peers: Vec<Peer>,
38}
39
40#[derive(Clone, Deserialize, PartialEq)]
42#[serde(rename_all = "camelCase")]
43#[non_exhaustive]
44pub struct Peer {
45 pub address: DittoAddress,
47
48 #[doc(hidden)]
49 #[deprecated(note = "use `.peer_key_string` instead")]
50 pub peer_key: Vec<u8>,
51
52 pub peer_key_string: String,
58
59 pub device_name: String,
62
63 pub os: Option<PresenceOs>,
65
66 #[doc(hidden)]
68 #[deprecated(
69 note = "This feature has been removed, this value will always be 0 from SDK release 4.8.0"
70 )]
71 #[serde(default, skip_deserializing)]
72 pub query_overlap_group: u8,
73
74 pub is_connected_to_ditto_cloud: bool,
79
80 pub is_compatible: Option<bool>,
85
86 pub ditto_sdk_version: Option<String>,
88
89 pub connections: Vec<Connection>,
91
92 pub peer_metadata: JsonValue,
103
104 pub identity_service_metadata: JsonValue,
110}
111
112impl ::core::fmt::Debug for Peer {
113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 f.debug_struct("Peer")
115 .field("address", &self.address)
116 .field("peer_key_string", &self.peer_key_string)
117 .field("device_name", &self.device_name)
118 .field("os", &self.os)
119 .field(
120 "is_connected_to_ditto_cloud",
121 &self.is_connected_to_ditto_cloud,
122 )
123 .field("is_compatible", &self.is_compatible)
124 .field("ditto_sdk_version", &self.ditto_sdk_version)
125 .field("connections", &self.connections)
126 .field("peer_metadata", &self.peer_metadata)
127 .field("identity_service_metadata", &self.identity_service_metadata)
128 .finish_non_exhaustive()
129 }
130}
131
132#[doc(hidden)]
139#[allow(unused_imports)]
140#[deprecated(note = "Use `Connection` instead")]
141pub use Connection as UndirectedConnection;
142
143#[derive(Clone, Serialize, Deserialize)]
145#[serde(rename_all = "camelCase")]
146#[non_exhaustive]
147pub struct Connection {
148 pub id: String,
154
155 #[doc(hidden)]
157 #[deprecated(note = "use `.peer_key_string1` instead")]
158 pub peer1: Vec<u8>,
159
160 #[doc(hidden)]
162 #[deprecated(note = "use `.peer_key_string2` instead")]
163 pub peer2: Vec<u8>,
164
165 pub peer_key_string1: String,
170
171 pub peer_key_string2: String,
176
177 pub connection_type: super::ConnectionType,
180
181 pub approximate_distance_in_meters: Option<f32>,
184}
185
186impl ::core::fmt::Debug for Connection {
187 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188 f.debug_struct("Connection")
189 .field("id", &self.id)
190 .field("peer_key_string1", &self.peer_key_string1)
191 .field("peer_key_string2", &self.peer_key_string2)
192 .field("connection_type", &self.connection_type)
193 .field(
194 "approximate_distance_in_meters",
195 &self.approximate_distance_in_meters,
196 )
197 .finish_non_exhaustive()
198 }
199}
200
201impl Eq for Connection {}
202impl PartialEq<Self> for Connection {
203 fn eq(&self, other: &Self) -> bool {
204 self.id == other.id
205 }
206}
207
208impl PartialOrd for Connection {
209 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
210 Some(self.cmp(other))
211 }
212}
213
214impl Ord for Connection {
215 fn cmp(&self, other: &Self) -> Ordering {
216 self.id.cmp(&other.id)
217 }
218}
219
220#[derive(PartialEq, Eq, Hash, Clone, Debug, Serialize, Deserialize)]
222pub enum PresenceOs {
223 #[serde(rename = "Generic")]
225 Generic,
226
227 #[serde(rename = "iOS")]
229 Ios,
230
231 #[serde(rename = "Android")]
233 Android,
234
235 #[serde(rename = "Linux")]
237 Linux,
238
239 #[serde(rename = "Windows")]
241 Windows,
242
243 #[serde(rename = "macOS")]
245 MacOS,
246}
247
248impl std::fmt::Display for PresenceOs {
249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250 match self {
251 PresenceOs::Generic => write!(f, "Generic"),
252 PresenceOs::Ios => write!(f, "iOS"),
253 PresenceOs::Android => write!(f, "Android"),
254 PresenceOs::Linux => write!(f, "Linux"),
255 PresenceOs::MacOS => write!(f, "macOS"),
256 PresenceOs::Windows => write!(f, "Windows"),
257 }
258 }
259}
260
261#[allow(deprecated)]
279pub use connection_type::ConnectionType;
280#[allow(deprecated)]
281mod connection_type {
282 use super::*;
283
284 #[doc(hidden)]
290 #[deprecated(note = "Use `ConnectionType` instead")]
291 #[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Copy, Debug, Serialize, Deserialize)]
292 pub enum ConnectionType {
293 Bluetooth,
294 AccessPoint,
295 P2PWiFi,
296 WebSocket,
297 }
298}
299
300#[cfg(test)]
301mod tests {
302 use super::*;
303
304 const V3_PEER_JSON: &str = r#"
305 {
306 "localPeer": {
307 "address": {"siteId":1, "pubkey":[1]},
308 "deviceName": "local-peer",
309 "peerKey": [1],
310 "peerKeyString": "pkAQ",
311 "peerMetadata": {},
312 "identityServiceMetadata": {},
313 "os": "macOS",
314 "isConnectedToDittoCloud": false,
315 "isCompatible": true,
316 "dittoSdkVersion": "1.0.0",
317 "meshRole": 0,
318 "queryOverlapGroup": 0,
319 "connections" : [
320 {
321 "id": "pkAQ<->pkAg:Bluetooth",
322 "peer1": [1],
323 "peer2": [2],
324 "peerKeyString1": "pkAQ",
325 "peerKeyString2": "pkAg",
326 "connectionType": "Bluetooth",
327 "approximateDistanceInMeters": null
328 },
329 {
330 "id": "pkAQ<->pkAw:AccessPoint",
331 "peer1": [1],
332 "peer2": [3],
333 "peerKeyString1": "pkAQ",
334 "peerKeyString2": "pkAw",
335 "connectionType": "AccessPoint",
336 "approximateDistanceInMeters": null
337 },
338 {
339 "id": "pkAQ<->pkBA:WebSocket",
340 "peer1": [1],
341 "peer2": [4],
342 "peerKeyString1": "pkAQ",
343 "peerKeyString2": "pkBA",
344 "connectionType": "WebSocket",
345 "approximateDistanceInMeters": null
346 }
347 ]
348 },
349 "remotePeers": [
350 {
351 "address": {"siteId":2, "pubkey":[2]},
352 "peerKey": [2],
353 "peerKeyString": "pkAG",
354 "peerMetadata": {},
355 "identityServiceMetadata": {},
356 "deviceName": "device-2",
357 "os": "iOS",
358 "isConnectedToDittoCloud": false,
359 "isCompatible": true,
360 "dittoSdkVersion": null,
361 "meshRole": 0,
362 "queryOverlapGroup": 0,
363 "connections" : [
364 {
365 "id": "pkAQ<->pkAg:Bluetooth",
366 "peer1": [1],
367 "peer2": [2],
368 "peerKeyString1": "pkAQ",
369 "peerKeyString2": "pkAg",
370 "connectionType": "Bluetooth",
371 "approximateDistanceInMeters": null
372 }
373 ]
374 },
375 {
376 "address": {"siteId":3, "pubkey":[3]},
377 "peerKey": [3],
378 "peerKeyString": "pkAw",
379 "peerMetadata": {},
380 "identityServiceMetadata": {},
381 "deviceName": "device-3",
382 "os": "Android",
383 "isConnectedToDittoCloud": false,
384 "isCompatible": true,
385 "dittoSdkVersion": "1.0.3",
386 "meshRole": 32,
387 "queryOverlapGroup": 32,
388 "connections" : [
389 {
390 "id": "pkAQ<->pkAw:AccessPoint",
391 "peer1": [1],
392 "peer2": [3],
393 "peerKeyString1": "pkAQ",
394 "peerKeyString2": "pkAw",
395 "connectionType": "AccessPoint",
396 "approximateDistanceInMeters": null
397 }
398 ]
399 },
400 {
401 "address": {"siteId":4, "pubkey":[4]},
402 "peerKey": [4],
403 "peerKeyString": "pkBA",
404 "peerMetadata": {},
405 "identityServiceMetadata": {},
406 "deviceName": "device-4",
407 "os": "Linux",
408 "isConnectedToDittoCloud": false,
409 "isCompatible": true,
410 "dittoSdkVersion": null,
411 "connections" : [
412 {
413 "id": "pkAQ<->pkBA:WebSocket",
414 "peer1": [1],
415 "peer2": [4],
416 "peerKeyString1": "pkAQ",
417 "peerKeyString2": "pkBA",
418 "connectionType": "WebSocket",
419 "approximateDistanceInMeters": null
420 }
421 ]
422 }
423 ]
424 }
425 "#;
426
427 #[test]
428 fn test_json_parsing() {
429 #![allow(deprecated)]
430
431 let graph: PresenceGraph = serde_json::from_str(V3_PEER_JSON).unwrap();
432
433 assert_eq!(graph.local_peer.address.pubkey, vec![1]);
435 assert_eq!(graph.local_peer.device_name, "local-peer");
436 assert_eq!(graph.local_peer.os, Some(PresenceOs::MacOS));
437 assert_eq!(graph.local_peer.query_overlap_group, 0);
438 assert_eq!(graph.local_peer.connections.len(), 3);
439 assert_eq!(graph.local_peer.peer_key, vec![1]);
440 assert_eq!(graph.local_peer.peer_key_string, "pkAQ");
441 assert_eq!(graph.local_peer.connections[0].peer_key_string1, "pkAQ");
442 assert_eq!(graph.local_peer.connections[0].peer_key_string2, "pkAg");
443 assert_eq!(graph.local_peer.connections[1].peer_key_string1, "pkAQ");
444 assert_eq!(graph.local_peer.connections[1].peer_key_string2, "pkAw");
445 assert_eq!(graph.local_peer.connections[2].peer_key_string1, "pkAQ");
446 assert_eq!(graph.local_peer.connections[2].peer_key_string2, "pkBA");
447
448 assert_eq!(graph.remote_peers[0].address.pubkey, vec![2]);
451 assert_eq!(graph.remote_peers[0].peer_key, vec![2]);
452 assert_eq!(graph.remote_peers[0].device_name, "device-2");
453 assert_eq!(graph.remote_peers[0].os, Some(PresenceOs::Ios));
454 let connections = &graph.remote_peers[0].connections;
455 assert_eq!(connections[0].peer_key_string1, "pkAQ");
456 assert_eq!(connections[0].peer_key_string2, "pkAg");
457
458 assert_eq!(graph.remote_peers[1].address.pubkey, vec![3]);
459 assert_eq!(graph.remote_peers[1].peer_key, vec![3]);
460 assert_eq!(graph.remote_peers[1].device_name, "device-3");
461 assert_eq!(graph.remote_peers[1].os, Some(PresenceOs::Android));
462 let connections = &graph.remote_peers[1].connections;
463 assert_eq!(connections[0].peer_key_string1, "pkAQ");
464 assert_eq!(connections[0].peer_key_string2, "pkAw");
465
466 assert_eq!(graph.remote_peers[2].address.pubkey, vec![4]);
467 assert_eq!(graph.remote_peers[2].peer_key, vec![4]);
468 assert_eq!(graph.remote_peers[2].device_name, "device-4");
469 assert_eq!(graph.remote_peers[2].os, Some(PresenceOs::Linux));
470 let connections = &graph.remote_peers[2].connections;
471 assert_eq!(connections[0].peer_key_string1, "pkAQ");
472 assert_eq!(connections[0].peer_key_string2, "pkBA");
473
474 #[allow(deprecated)]
475 {
476 assert_eq!(graph.local_peer.query_overlap_group, 0);
477 assert_eq!(graph.remote_peers[0].query_overlap_group, 0);
478 assert_eq!(graph.remote_peers[1].query_overlap_group, 0);
479 assert_eq!(graph.remote_peers[2].query_overlap_group, 0);
480 }
481 }
482}