hfile 0.5.0

Hfile is a command-line interface (CLI) tool for generating hash values of files
Documentation
use anyhow::{Context, Result, bail};
use clap::Parser;
use hfile::{checksum, command::Args, hash, walkdir};
use path_clean::PathClean;
use std::{fs, path::Path};

fn check_checksums(args: &Args, checksum_file: &str) -> Result<()> {
    checksum::check(Path::new(checksum_file), args.algorithm)?;
    Ok(())
}

fn hash_single_file(args: &Args) -> Result<()> {
    let Some(file_arg) = &args.file else {
        return Ok(());
    };

    let file = Path::new(file_arg);
    let metadata = fs::metadata(file)
        .with_context(|| format!("failed to read metadata for {}", file.display()))?;
    if metadata.is_dir() {
        bail!("Use option -p to pass a directory");
    }

    let hash = hash::hash_file(args.algorithm, file)?;
    if args.size {
        println!(
            "{}\t{}\t{}",
            hash,
            file.clean().display(),
            bytesize::to_string(metadata.len(), true)
        );
    } else {
        println!("{hash}\t{}", file.clean().display());
    }

    Ok(())
}

fn hash_path(args: &Args, path: &str) -> Result<()> {
    if args.duplicates {
        let duplicates = walkdir::find_duplicates(path, args.algorithm)?;
        for (index, (hash, paths)) in duplicates.iter().enumerate() {
            println!("{hash}");
            for path in paths {
                println!("\t{}", path.clean().display());
            }

            if index + 1 < duplicates.len() {
                println!();
            }
        }
    } else {
        walkdir::read(path, args.algorithm, args.size)?;
    }

    Ok(())
}

fn run() -> Result<()> {
    let args = Args::parse();

    if let Some(checksum_file) = &args.check {
        check_checksums(&args, checksum_file)
    } else {
        match &args.path {
            Some(path) => hash_path(&args, path),
            None => hash_single_file(&args),
        }
    }
}

fn main() {
    if let Err(error) = run() {
        eprintln!("{error}");
        std::process::exit(1);
    }
}