1use anyhow::Context;
23use oxibrain::{vault, vault::SyncReport};
24#[cfg(unix)]
25use oxibrain_client::default_socket_path;
26use std::path::Path;
27
28pub async fn run(dir: &Path, root: &Path, space: &str) -> anyhow::Result<()> {
29 let report = match oxibrain::Brain::open(oxibrain::BrainConfig::at(dir)).await {
30 Ok(brain) => vault::sync_vault(&brain, root, space).await?,
31 Err(oxibrain::BrainError::Locked { holder }) => {
32 eprintln!("note: store locked ({holder}); attaching to the daemon socket");
33 run_via_daemon(root, space).await?
34 }
35 Err(e) => return Err(e.into()),
36 };
37 print_report(&report);
38 Ok(())
39}
40
41async fn run_via_daemon(root: &Path, space: &str) -> anyhow::Result<SyncReport> {
45 let socket = socket_path()?;
46 let mut client = oxibrain_client::BrainClient::connect(&socket)
47 .await
48 .with_context(|| format!("attach to daemon at {}", socket.display()))?;
49 let out = client
50 .sync_run(&root.to_string_lossy(), space)
51 .await
52 .context("sync/run on daemon")?;
53 Ok(SyncReport {
54 new: out.new,
55 modified: out.modified,
56 unchanged: out.unchanged,
57 })
58}
59
60#[cfg(unix)]
63fn socket_path() -> anyhow::Result<std::path::PathBuf> {
64 default_socket_path().ok_or_else(|| {
65 anyhow::anyhow!("no daemon socket: $OXIBRAIN_SOCKET unset and $HOME unavailable")
66 })
67}
68
69#[cfg(not(unix))]
70fn socket_path() -> anyhow::Result<std::path::PathBuf> {
71 anyhow::bail!("daemon attach is only supported on Unix")
72}
73
74fn print_report(report: &SyncReport) {
75 if !report.new.is_empty() {
76 for p in &report.new {
77 println!(" new: {p}");
78 }
79 }
80 if !report.modified.is_empty() {
81 for p in &report.modified {
82 println!(" modified: {p}");
83 }
84 }
85 println!(
86 "sync complete: {} new, {} unchanged, {} modified",
87 report.new.len(),
88 report.unchanged.len(),
89 report.modified.len()
90 );
91}