binhex-rs 0.1.0

Crate to read BinHex 4 encoded files
Documentation
use std::{env, fs, path::Path, process};

use walkdir::{DirEntry, WalkDir};

fn main() {
    pretty_env_logger::init();
    log::debug!("Startup");
    let Some(path) = env::args().nth(1) else {
        eprintln!("Please provide a path to an hqx file as the first arugment");
        process::exit(-1);
    };

    match fs::metadata(&path) {
        Ok(meta) if meta.is_dir() => WalkDir::new(path)
            .into_iter()
            .filter_entry(|e| !is_hidden(e))
            .filter_map(|e| e.ok())
            .for_each(|f| match extract_file(f.path()) {
                Ok(_) => {}
                Err(e) => {
                    eprintln!(
                        "Could not fully extract file at {}: {}",
                        f.path().display(),
                        e
                    );
                }
            }),
        Ok(meta) if meta.is_file() => match extract_file(&path) {
            Ok(_) => {}
            Err(e) => {
                eprintln!("Could not extract file at {}", path);
                eprintln!("{:?}", e);
                process::exit(-3);
            }
        },
        Ok(_) => {
            eprintln!("Path is neither file nor directory, skipping analysis…");
        }
        Err(e) => {
            eprintln!("Could not determine if path is a file or directory!");
            eprintln!("{:?}", e);
            process::exit(-2);
        }
    }

    log::debug!("Shutdown");
}

fn extract_file(path: impl AsRef<Path>) -> Result<(), binhex::Error> {
    log::info!("Extracting {}", path.as_ref().display());
    let mut archive = match binhex::Archive::open(path.as_ref()) {
        Ok(i) => i,
        Err(e) => {
            eprintln!("Could not open archive at {}", path.as_ref().display());
            eprintln!("{:?}", e);
            return Err(e);
        }
    };

    if archive.data_len() != 0 {
        let output_name = archive.name().replace("/", ":");
        let mut input = archive.data_fork()?;
        let mut output = std::fs::File::create_new(output_name)?;
        std::io::copy(&mut input, &mut output)?;
    }

    if archive.resource_len() != 0 {
        let output_name = format!("{}.rsrc", archive.name().replace("/", ":"));
        let mut input = archive.resource_fork()?;
        let mut output = std::fs::File::create_new(output_name)?;
        std::io::copy(&mut input, &mut output)?;
    }

    Ok(())
}

fn is_hidden(entry: &DirEntry) -> bool {
    entry
        .file_name()
        .to_str()
        .map(|s| s.starts_with(""))
        .unwrap_or(false)
}