bitbottle 0.10.0

a modern archive file format
Documentation
use clap::{arg, value_parser, Command};
use std::path::PathBuf;
use bitbottle::{FileScanner, HashType, ScanState};
use bitbottle::cli::{ProgressLine, to_binary_si};


pub fn main() {
    let args = Command::new("buzscan")
        .version("1.0")
        .author("Robey Pointer <robey@lag.net>")
        .about(
"Scans and hashes a list of files and folders (recursively) to determine
how much of the data is unique and how much is duplicated."
        )
        .args([
            arg!(-v --verbose           "list each file's metadata at the end"),
            arg!(--blake2               "use blake2 hashing instead of sha-256"),
            arg!(--blake3               "use blake3 hashing instead of sha-256"),
            arg!(<PATH> ...             "path(s) to scan").required(true).value_parser(value_parser!(PathBuf)),
        ])
        .get_matches();

    let mut hash_type = HashType::SHA256;
    if args.get_flag("blake2") {
        hash_type = HashType::BLAKE2;
    }
    if args.get_flag("blake3") {
        hash_type = HashType::BLAKE2;
    }

    let progress = ProgressLine::new().to_shared();
    let mut file_scanner = FileScanner::new(hash_type, 18, 20, 22, 10, vec![0u8; 65536], |state| {
        let mut progress = progress.borrow_mut();
        match state {
            ScanState::FileList { file_count, bytes, .. } => {
                progress.show_bar = false;
                progress.update(0f64, format!("Scanning files: {:>8} files, disk space: {:>5}",
                    file_count, to_binary_si(bytes as f64)));
            },
            ScanState::Blocks { file_count, bytes, total_bytes, unique_bytes, .. } => {
                progress.show_bar = true;
                let percent = bytes as f64 / total_bytes as f64;
                progress.update(percent, format!("{:>8} files, {:>5}/{:>5}, {:>5} unique",
                    file_count, to_binary_si(bytes as f64), to_binary_si(total_bytes as f64),
                    to_binary_si(unique_bytes as f64)));
                if bytes == total_bytes {
                    progress.force_update();
                }
            }
        }
        progress.display();
    });

    file_scanner.scan_paths(&args.get_many::<PathBuf>("PATH").unwrap().map(PathBuf::from).collect::<Vec<_>>()).unwrap();
    let file_list = file_scanner.build_block_list(hash_type).unwrap();

    if args.get_flag("verbose") {
        for atlas in &file_list.files {
            let atlas = atlas.borrow();
            println!("{:>5} {:>3o} {:>8} {:>8} {:>64} {}",
                if atlas.is_folder { "".to_string() } else { to_binary_si(atlas.size as f64) },
                atlas.perms & 0x1ff,
                atlas.user,
                atlas.group,
                if atlas.is_folder { "".to_string() } else { hex::encode(atlas.contents.hash) },
                atlas.path.as_path().display()
            );
        }
    }
}