use std::io::Write;
use std::time::Instant;
use anyhow::Result;
use crate::checksum;
use crate::cli::{BackupArgs, GlobalArgs};
use crate::error::AtomwriteError;
use crate::ndjson_types::BackupResult;
use crate::output::NdjsonWriter;
pub fn cmd_backup(
args: &BackupArgs,
global: &GlobalArgs,
writer: &mut NdjsonWriter<impl Write>,
) -> Result<()> {
let start = Instant::now();
let workspace = global.resolve_workspace()?;
let mut backed_up = 0u64;
let mut total_bytes = 0u64;
for path in &args.paths {
let source = crate::path_safety::validate_path(path, &workspace)?;
if !source.exists() {
return Err(AtomwriteError::NotFound {
path: source.clone(),
}
.into());
}
if !source.is_file() {
tracing::warn!(path = %source.display(), "skipping non-file path");
continue;
}
let file_start = Instant::now();
let hash = checksum::hash_file(&source)?;
let bytes = std::fs::metadata(&source)?.len();
if args.dry_run {
writer.write_event(&serde_json::json!({
"type": "plan",
"operation": "backup",
"path": source.display().to_string(),
"bytes": bytes,
"checksum": hash,
}))?;
continue;
}
let backup_path = crate::atomic::create_backup(&source, args.retention)?;
writer.write_event(&BackupResult {
r#type: "backup",
path: source.display().to_string(),
backup_path: backup_path.display().to_string(),
checksum: hash,
bytes,
elapsed_ms: file_start.elapsed().as_millis() as u64,
})?;
backed_up += 1;
total_bytes += bytes;
}
writer.write_event(&serde_json::json!({
"type": "summary",
"files_backed_up": backed_up,
"total_bytes": total_bytes,
"dry_run": args.dry_run,
"elapsed_ms": start.elapsed().as_millis() as u64,
}))?;
Ok(())
}