chrs_sync/lib.rs
1use chrono::Utc;
2/// chrs-sync crate provides synchronization utilities for the CHORUS system.
3///
4/// It uses a `Mailbox` for message passing between peers and a Dolt repository
5/// to track state hashes. The primary abstraction is `SyncManager`, which can
6/// broadcast the current repository hash to peers and handle incoming sync
7/// signals.
8use chrs_mail::{Mailbox, Message};
9use std::path::PathBuf;
10use std::process::Command;
11use uuid::Uuid;
12
13/// Manages synchronization of a Dolt repository across peers.
14///
15/// # Fields
16/// * `mailbox` – The `Mailbox` instance used to send and receive messages.
17/// * `repo_path` – Filesystem path to the local Dolt repository.
18///
19/// # Rationale
20/// The CHORUS architecture relies on deterministic state replication. By
21/// broadcasting the latest commit hash (`sync_signal`) each peer can decide
22/// whether to pull updates. This struct encapsulates that behaviour, keeping the
23/// rest of the system agnostic of the underlying VCS commands.
24pub struct SyncManager {
25 mailbox: Mailbox,
26 repo_path: PathBuf,
27}
28
29impl SyncManager {
30 /// Creates a new `SyncManager`.
31 ///
32 /// # Parameters
33 /// * `mailbox` – An already‑opened `Mailbox` for peer communication.
34 /// * `repo_path` – Path to the Dolt repository that should be kept in sync.
35 ///
36 /// Returns a fully‑initialised manager ready to broadcast or handle sync
37 /// signals.
38 pub fn new(mailbox: Mailbox, repo_path: PathBuf) -> Self {
39 Self { mailbox, repo_path }
40 }
41
42 /// Broadcasts the current repository state to a remote peer.
43 ///
44 /// The method executes `dolt log -n 1 --format %H` to obtain the most recent
45 /// commit hash, constructs a `Message` with topic `"sync_signal"` and sends it
46 /// via the mailbox.
47 ///
48 /// * `from_peer` – Identifier of the sender.
49 /// * `to_peer` – Identifier of the intended recipient.
50 ///
51 /// # Errors
52 /// Returns any I/O or command‑execution error wrapped in a boxed `dyn
53 /// Error`.
54 pub fn broadcast_state(
55 &self,
56 from_peer: &str,
57 to_peer: &str,
58 ) -> Result<(), Box<dyn std::error::Error>> {
59 // Get current dolt hash
60 let output = Command::new("dolt")
61 .args(&["log", "-n", "1", "--format", "%H"])
62 .current_dir(&self.repo_path)
63 .output()?;
64
65 let current_hash = String::from_utf8_lossy(&output.stdout).trim().to_string();
66
67 let msg = Message {
68 id: Uuid::new_v4(),
69 from_peer: from_peer.into(),
70 to_peer: to_peer.into(),
71 topic: "sync_signal".into(),
72 payload: serde_json::json!({ "commit_hash": current_hash }),
73 sent_at: Utc::now(),
74 read_at: None,
75 };
76
77 self.mailbox.send(&msg)?;
78 println!(
79 "Broadcasted sync signal: {} from {}",
80 current_hash, from_peer
81 );
82 Ok(())
83 }
84
85 /// Handles an incoming `sync_signal` message.
86 ///
87 /// If the message topic is not `"sync_signal"` the function returns `Ok(())`
88 /// immediately. Otherwise it extracts the remote commit hash and attempts a
89 /// `dolt pull origin` to bring the local repository up‑to‑date. In a real
90 /// P2P deployment the remote URL would be derived from the sender, but the
91 /// current implementation uses the default remote configuration.
92 ///
93 /// # Errors
94 /// Propagates any command execution failures.
95 pub fn handle_sync_signal(&self, msg: &Message) -> Result<(), Box<dyn std::error::Error>> {
96 if msg.topic != "sync_signal" {
97 return Ok(());
98 }
99
100 let remote_hash = msg.payload["commit_hash"].as_str().unwrap_or_default();
101 println!("Received sync signal for hash: {}", remote_hash);
102
103 // In a real P2P scenario, we would pull from the remote peer's URL.
104 // For now, we simulate by attempting a 'dolt pull' if a remote is configured.
105 let status = Command::new("dolt")
106 .args(&["pull", "origin"])
107 .current_dir(&self.repo_path)
108 .status()?;
109
110 if status.success() {
111 println!("Successfully pulled updates for hash: {}", remote_hash);
112 }
113
114 Ok(())
115 }
116}