use blazehash::algorithm::Algorithm;
use clap::Parser;
use std::path::PathBuf;
use std::str::FromStr;
#[derive(Parser, Debug)]
#[command(
name = "blazehash",
version,
about = "Forensic file hasher — hashdeep for the modern era"
)]
pub struct Cli {
#[arg(required_unless_present = "version")]
pub paths: Vec<PathBuf>,
#[arg(short = 'c', long = "compute", value_parser = parse_algorithms, default_value = "blake3")]
pub algorithms: Vec<Vec<Algorithm>>,
#[arg(short = 'r', long = "recursive")]
pub recursive: bool,
#[arg(short = 'o', long = "output")]
pub output: Option<PathBuf>,
#[arg(short = 'a', long = "audit")]
pub audit: bool,
#[arg(short = 'k', long = "known")]
pub known: Vec<PathBuf>,
#[arg(short = 's', long = "size-only")]
pub size_only: bool,
#[arg(short = 'b', long = "bare")]
pub bare: bool,
#[arg(short = 'p', long = "piecewise")]
pub piecewise: Option<String>,
#[arg(long = "resume")]
pub resume: bool,
#[arg(long = "format", default_value = "hashdeep")]
pub format: String,
}
pub fn parse_chunk_size(s: &str) -> Result<usize, String> {
let s = s.trim();
let (num_str, multiplier) = if s.ends_with('G') || s.ends_with('g') {
(&s[..s.len() - 1], 1024 * 1024 * 1024)
} else if s.ends_with('M') || s.ends_with('m') {
(&s[..s.len() - 1], 1024 * 1024)
} else if s.ends_with('K') || s.ends_with('k') {
(&s[..s.len() - 1], 1024)
} else {
(s, 1usize)
};
let num: usize = num_str
.parse()
.map_err(|e: std::num::ParseIntError| e.to_string())?;
Ok(num * multiplier)
}
fn parse_algorithms(s: &str) -> Result<Vec<Algorithm>, String> {
s.split(',')
.map(|name| Algorithm::from_str(name.trim()).map_err(|e| e.to_string()))
.collect()
}
#[derive(Debug)]
pub enum Mode {
SizeOnly,
Audit,
Piecewise,
Hash,
}
impl Cli {
pub fn flat_algorithms(&self) -> Vec<Algorithm> {
let flat: Vec<Algorithm> = self.algorithms.iter().flatten().copied().collect();
if flat.is_empty() {
vec![Algorithm::Blake3]
} else {
flat
}
}
pub fn mode(&self) -> Mode {
if self.size_only {
Mode::SizeOnly
} else if self.audit {
Mode::Audit
} else if self.piecewise.is_some() {
Mode::Piecewise
} else {
Mode::Hash
}
}
}