1use std::cmp::Ordering;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value as JsonValue;
5
6use crate::presence::ConnectionType;
7#[cfg(doc)]
8use crate::transport::Presence;
9
10#[allow(deprecated)]
12mod ditto_address {
13 use serde::{Deserialize, Serialize};
14
15 #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
16 #[serde(rename_all = "camelCase")]
18 pub struct DittoAddress {
19 pub(crate) pubkey: Vec<u8>,
20 #[deprecated]
22 #[doc(hidden)]
23 #[serde(default)]
24 pub(crate) site_id: Option<u64>,
25 }
26}
27
28#[derive(Debug, Clone, Deserialize, PartialEq)]
30#[serde(rename_all = "camelCase")]
31#[non_exhaustive]
32pub struct PresenceGraph {
33 pub local_peer: Peer,
35 pub remote_peers: Vec<Peer>,
42}
43
44#[derive(Clone, Deserialize, PartialEq)]
46#[serde(rename_all = "camelCase")]
47#[non_exhaustive]
48pub struct Peer {
49 pub address: ditto_address::DittoAddress,
51
52 #[serde(rename = "peerKeyString")]
58 pub peer_key: String,
59
60 pub device_name: String,
63
64 pub os: Option<PresenceOs>,
66
67 #[serde(alias = "isConnectedToDittoCloud")]
72 pub is_connected_to_ditto_server: bool,
73
74 pub is_compatible: Option<bool>,
79
80 pub ditto_sdk_version: Option<String>,
82
83 pub connections: Vec<Connection>,
85
86 pub peer_metadata: JsonValue,
97
98 pub identity_service_metadata: JsonValue,
104}
105
106impl ::core::fmt::Debug for Peer {
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 f.debug_struct("Peer")
109 .field("address", &self.address)
110 .field("peer_key", &self.peer_key)
111 .field("device_name", &self.device_name)
112 .field("os", &self.os)
113 .field(
114 "is_connected_to_ditto_server",
115 &self.is_connected_to_ditto_server,
116 )
117 .field("is_compatible", &self.is_compatible)
118 .field("ditto_sdk_version", &self.ditto_sdk_version)
119 .field("connections", &self.connections)
120 .field("peer_metadata", &self.peer_metadata)
121 .field("identity_service_metadata", &self.identity_service_metadata)
122 .finish_non_exhaustive()
123 }
124}
125
126#[derive(Clone, Serialize, Deserialize)]
128#[serde(rename_all = "camelCase")]
129#[non_exhaustive]
130pub struct Connection {
131 pub id: String,
137
138 #[serde(rename = "peerKeyString1")]
143 pub peer1: String,
144
145 #[serde(rename = "peerKeyString2")]
150 pub peer2: String,
151
152 pub connection_type: ConnectionType,
155}
156
157impl ::core::fmt::Debug for Connection {
158 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159 f.debug_struct("Connection")
160 .field("id", &self.id)
161 .field("peer1", &self.peer1)
162 .field("peer2", &self.peer2)
163 .field("connection_type", &self.connection_type)
164 .finish_non_exhaustive()
165 }
166}
167
168impl Eq for Connection {}
169impl PartialEq<Self> for Connection {
170 fn eq(&self, other: &Self) -> bool {
171 self.id == other.id
172 }
173}
174
175impl PartialOrd for Connection {
176 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
177 Some(self.cmp(other))
178 }
179}
180
181impl Ord for Connection {
182 fn cmp(&self, other: &Self) -> Ordering {
183 self.id.cmp(&other.id)
184 }
185}
186
187#[derive(PartialEq, Eq, Hash, Clone, Debug, Serialize, Deserialize)]
189pub enum PresenceOs {
190 #[serde(rename = "Generic")]
192 Generic,
193
194 #[serde(rename = "iOS")]
196 Ios,
197
198 #[serde(rename = "Android")]
200 Android,
201
202 #[serde(rename = "Linux")]
204 Linux,
205
206 #[serde(rename = "Windows")]
208 Windows,
209
210 #[serde(rename = "macOS")]
212 MacOS,
213}
214
215impl std::fmt::Display for PresenceOs {
216 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217 match self {
218 PresenceOs::Generic => write!(f, "Generic"),
219 PresenceOs::Ios => write!(f, "iOS"),
220 PresenceOs::Android => write!(f, "Android"),
221 PresenceOs::Linux => write!(f, "Linux"),
222 PresenceOs::MacOS => write!(f, "macOS"),
223 PresenceOs::Windows => write!(f, "Windows"),
224 }
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231
232 const V3_PEER_JSON: &str = r#"
233 {
234 "localPeer": {
235 "address": {"pubkey":[1]},
236 "deviceName": "local-peer",
237 "peerKeyString": "pkAQ",
238 "peerMetadata": {},
239 "identityServiceMetadata": {},
240 "os": "macOS",
241 "isConnectedToDittoCloud": false,
242 "isCompatible": true,
243 "dittoSdkVersion": "1.0.0",
244 "connections" : [
245 {
246 "id": "pkAQ<->pkAg:Bluetooth",
247 "peerKeyString1": "pkAQ",
248 "peerKeyString2": "pkAg",
249 "connectionType": "Bluetooth"
250 },
251 {
252 "id": "pkAQ<->pkAw:AccessPoint",
253 "peerKeyString1": "pkAQ",
254 "peerKeyString2": "pkAw",
255 "connectionType": "AccessPoint"
256 },
257 {
258 "id": "pkAQ<->pkBA:WebSocket",
259 "peerKeyString1": "pkAQ",
260 "peerKeyString2": "pkBA",
261 "connectionType": "WebSocket"
262 }
263 ]
264 },
265 "remotePeers": [
266 {
267 "address": {"pubkey":[2]},
268 "peerKeyString": "pkAG",
269 "peerMetadata": {},
270 "identityServiceMetadata": {},
271 "deviceName": "device-2",
272 "os": "iOS",
273 "isConnectedToDittoCloud": false,
274 "isCompatible": true,
275 "dittoSdkVersion": null,
276 "connections" : [
277 {
278 "id": "pkAQ<->pkAg:Bluetooth",
279 "peerKeyString1": "pkAQ",
280 "peerKeyString2": "pkAg",
281 "connectionType": "Bluetooth"
282 }
283 ]
284 },
285 {
286 "address": {"pubkey":[3]},
287 "peerKeyString": "pkAw",
288 "peerMetadata": {},
289 "identityServiceMetadata": {},
290 "deviceName": "device-3",
291 "os": "Android",
292 "isConnectedToDittoCloud": false,
293 "isCompatible": true,
294 "dittoSdkVersion": "1.0.3",
295 "connections" : [
296 {
297 "id": "pkAQ<->pkAw:AccessPoint",
298 "peerKeyString1": "pkAQ",
299 "peerKeyString2": "pkAw",
300 "connectionType": "AccessPoint"
301 }
302 ]
303 },
304 {
305 "address": {"pubkey":[4]},
306 "peerKeyString": "pkBA",
307 "peerMetadata": {},
308 "identityServiceMetadata": {},
309 "deviceName": "device-4",
310 "os": "Linux",
311 "isConnectedToDittoCloud": true,
312 "isCompatible": true,
313 "dittoSdkVersion": null,
314 "connections" : [
315 {
316 "id": "pkAQ<->pkBA:WebSocket",
317 "peerKeyString1": "pkAQ",
318 "peerKeyString2": "pkBA",
319 "connectionType": "WebSocket"
320 }
321 ]
322 }
323 ]
324 }
325 "#;
326
327 #[test]
328 fn test_json_parsing() {
329 #![allow(deprecated)]
330
331 let graph: PresenceGraph = serde_json::from_str(V3_PEER_JSON).unwrap();
332
333 assert_eq!(graph.local_peer.address.pubkey, vec![1]);
335 assert_eq!(graph.local_peer.device_name, "local-peer");
336 assert_eq!(graph.local_peer.os, Some(PresenceOs::MacOS));
337 assert!(!graph.local_peer.is_connected_to_ditto_server);
338 assert_eq!(graph.local_peer.connections.len(), 3);
339 assert_eq!(graph.local_peer.peer_key, "pkAQ");
340 assert_eq!(graph.local_peer.connections[0].peer1, "pkAQ");
341 assert_eq!(graph.local_peer.connections[0].peer2, "pkAg");
342 assert_eq!(graph.local_peer.connections[1].peer1, "pkAQ");
343 assert_eq!(graph.local_peer.connections[1].peer2, "pkAw");
344 assert_eq!(graph.local_peer.connections[2].peer1, "pkAQ");
345 assert_eq!(graph.local_peer.connections[2].peer2, "pkBA");
346
347 assert_eq!(graph.remote_peers[0].address.pubkey, vec![2]);
350 assert_eq!(graph.remote_peers[0].device_name, "device-2");
351 assert_eq!(graph.remote_peers[0].os, Some(PresenceOs::Ios));
352 assert!(!graph.remote_peers[0].is_connected_to_ditto_server);
353 let connections = &graph.remote_peers[0].connections;
354 assert_eq!(connections[0].peer1, "pkAQ");
355 assert_eq!(connections[0].peer2, "pkAg");
356
357 assert_eq!(graph.remote_peers[1].address.pubkey, vec![3]);
358 assert_eq!(graph.remote_peers[1].device_name, "device-3");
359 assert_eq!(graph.remote_peers[1].os, Some(PresenceOs::Android));
360 assert!(!graph.remote_peers[1].is_connected_to_ditto_server);
361 let connections = &graph.remote_peers[1].connections;
362 assert_eq!(connections[0].peer1, "pkAQ");
363 assert_eq!(connections[0].peer2, "pkAw");
364
365 assert_eq!(graph.remote_peers[2].address.pubkey, vec![4]);
366 assert_eq!(graph.remote_peers[2].device_name, "device-4");
367 assert_eq!(graph.remote_peers[2].os, Some(PresenceOs::Linux));
368 assert!(graph.remote_peers[2].is_connected_to_ditto_server);
369 let connections = &graph.remote_peers[2].connections;
370 assert_eq!(connections[0].peer1, "pkAQ");
371 assert_eq!(connections[0].peer2, "pkBA");
372 }
373}