use std::path::{Path, PathBuf};
use std::process::ExitCode;
use clap::Parser;
use logmv::{Op, run};
#[derive(Parser)]
#[command(author, version, about = "Logged atomic file move/trash")]
struct Cli {
log: String,
#[arg(long)]
trash: Option<String>,
#[arg(long)]
mkdir: bool,
#[arg(long)]
rmdir: bool,
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
rest: Vec<String>,
}
fn main() -> ExitCode {
let cli = Cli::parse();
match dispatch(cli) {
Ok(()) => ExitCode::SUCCESS,
Err(e) => {
eprintln!("logmv: {e}");
ExitCode::FAILURE
}
}
}
fn dispatch(cli: Cli) -> Result<(), Box<dyn std::error::Error>> {
let log = Path::new(&cli.log);
if let Some(trash_path) = &cli.trash {
let pairs = pair_up(&cli.rest)?;
let home_raw = std::env::var("HOME")?;
let home = PathBuf::from(&home_raw);
if !home.is_absolute() {
return Err(format!("HOME must be an absolute path, got: {home_raw:?}").into());
}
let trash_dir = home.join(".Trash");
run(
Op::Trash {
path: PathBuf::from(trash_path),
trash_dir,
},
log,
&pairs,
cli.mkdir,
cli.rmdir,
)?;
} else {
if cli.rest.len() < 2 {
return Err("usage: logmv <LOG> <SRC> <DST> [K V]...".into());
}
let src = PathBuf::from(&cli.rest[0]);
let dst = PathBuf::from(&cli.rest[1]);
let pairs = pair_up(&cli.rest[2..])?;
run(Op::Move { src, dst }, log, &pairs, cli.mkdir, cli.rmdir)?;
}
Ok(())
}
fn pair_up(slab: &[String]) -> Result<Vec<(&str, &str)>, Box<dyn std::error::Error>> {
if let Some(tok) = slab.iter().find(|t| t.starts_with("--")) {
return Err(format!(
"trailing metadata must not start with `--`: got {tok:?}; flags must precede SRC/PATH"
)
.into());
}
if !slab.chunks_exact(2).remainder().is_empty() {
return Err("trailing metadata must be [K V] pairs: got an odd number of arguments".into());
}
Ok(slab
.chunks_exact(2)
.map(|c| (c[0].as_str(), c[1].as_str()))
.collect())
}