use std::path::PathBuf;
use forgedb_coordinator::server::{CoordFsync, Coordinator, ServerError};
use crate::error::{CliError, Result};
pub struct CoordinateOptions {
pub root: PathBuf,
pub socket: Option<PathBuf>,
pub fsync: Option<String>,
pub fsync_interval: Option<u64>,
}
fn resolve_fsync(opts: &CoordinateOptions) -> Result<CoordFsync> {
let mode = opts
.fsync
.clone()
.or_else(|| std::env::var("FORGEDB_COORDINATOR_FSYNC").ok())
.unwrap_or_else(|| "always".to_string())
.to_lowercase();
let interval = opts
.fsync_interval
.or_else(|| {
std::env::var("FORGEDB_COORDINATOR_FSYNC_INTERVAL")
.ok()
.and_then(|s| s.parse().ok())
})
.unwrap_or(64);
match mode.as_str() {
"always" => Ok(CoordFsync::Always),
"never" => Ok(CoordFsync::Never),
"periodic" => Ok(CoordFsync::Periodic(interval.max(1))),
other => Err(CliError::Other(format!(
"invalid --fsync '{other}' — expected always | never | periodic"
))),
}
}
pub fn run(opts: CoordinateOptions) -> Result<()> {
let socket_path = opts
.socket
.clone()
.unwrap_or_else(|| opts.root.join("_coord.sock"));
let fsync = resolve_fsync(&opts)?;
eprintln!(
"forgedb-coordinator: starting on root={} socket={} fsync={fsync:?}",
opts.root.display(),
socket_path.display()
);
let coord = Coordinator::open_with_fsync(&opts.root, &socket_path, fsync).map_err(|e| match e {
ServerError::DirAlreadyLocked => CliError::Other(format!(
"another coordinator is already running on {}",
opts.root.display()
)),
ServerError::Io(io_err) => CliError::Io(io_err),
ServerError::Shutdown => CliError::Other("coordinator shut down unexpectedly".into()),
})?;
eprintln!(
"forgedb-coordinator: ready — listening on {}",
socket_path.display()
);
let coord_arc = std::sync::Arc::new(coord);
let coord_clone = std::sync::Arc::clone(&coord_arc);
ctrlc::set_handler(move || {
eprintln!("\nforgedb-coordinator: shutting down…");
coord_clone.shutdown();
})
.ok();
coord_arc.run().map_err(|e| CliError::Other(format!("coordinator run error: {e}")))?;
eprintln!("forgedb-coordinator: stopped.");
Ok(())
}