coven_core/sync/status.rs
1/// Sync status derived from device heads.
2///
3/// After each pull, the caller has the full list of `DeviceHead`s. This
4/// module provides a type to summarize that into a human-readable status
5/// for the UI: when we last synced, and what other devices are doing.
6use super::store_pull::VerifiedStoreDeviceHead;
7
8/// Activity summary for a single remote device.
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct DeviceActivity {
11 pub device_id: String,
12 /// Hex-encoded Ed25519 public key the device's head verified against — the
13 /// member the device belongs to. Empty only for a head that carried no author.
14 pub author: String,
15 pub last_seq: u64,
16 /// RFC 3339 timestamp of the device's last sync. None if the head
17 /// carried no timestamp.
18 pub last_sync: Option<String>,
19}
20
21/// Sync status derived from the heads fetched during a pull.
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct SyncStatus {
24 /// When this device last synced (RFC 3339). None if never synced.
25 pub last_sync_time: Option<String>,
26 /// Activity of other devices.
27 pub other_devices: Vec<DeviceActivity>,
28}
29
30/// Build a `SyncStatus` from a list of device heads.
31///
32/// `our_device_id` identifies the local device so its head can be
33/// separated from the "other devices" list.
34/// `local_sync_time` is the RFC 3339 timestamp of when *we* last
35/// completed a sync cycle (tracked locally, not from the heads).
36pub fn build_sync_status(
37 heads: &[VerifiedStoreDeviceHead],
38 our_device_id: &str,
39 local_sync_time: Option<&str>,
40) -> SyncStatus {
41 let mut other_devices: Vec<DeviceActivity> = Vec::new();
42
43 for head in heads {
44 if head.author.device_id.to_string() == our_device_id {
45 continue;
46 }
47
48 let activity = DeviceActivity {
49 device_id: head.author.device_id.to_string(),
50 author: head.author.author_pubkey.clone(),
51 last_seq: head.head.slot_sequence(),
52 last_sync: None,
53 };
54 match other_devices
55 .iter_mut()
56 .find(|current| current.device_id == activity.device_id)
57 {
58 Some(current) if current.last_seq < activity.last_seq => *current = activity,
59 Some(_) => {}
60 None => other_devices.push(activity),
61 }
62 }
63
64 SyncStatus {
65 last_sync_time: local_sync_time.map(|s| s.to_string()),
66 other_devices,
67 }
68}