hpk 0.3.9

HPK archiver for Haemimont Engine game files (Tropico 3-5, Omerta, Victor Vran, Surviving Mars etc.)
Documentation
use std::fs;
use std::path::Path;

use clap::{App, Arg, ArgMatches, SubCommand};
use glob::Pattern;

use crate::CliResult;

pub fn clap<'a, 'b>() -> App<'a, 'b> {
    #[allow(clippy::needless_pass_by_value)]
    fn validate_input(value: String) -> Result<(), String> {
        match fs::metadata(value) {
            Ok(ref md) if md.is_file() => Ok(()),
            Ok(_) => Err(String::from("Not a valid file")),
            Err(_) => Err(String::from("Not a valid file")),
        }
    }

    SubCommand::with_name("list")
        .about("List the content of a hpk archive")
        .display_order(20)
        .arg(Arg::from_usage("<file> 'hpk archive'").validator(validate_input))
        .arg(Arg::from_usage("[paths]..."))
}

pub fn execute(matches: &ArgMatches<'_>) -> CliResult {
    let input = value_t!(matches, "file", String)?;
    let paths = values_t!(matches, "paths", String).unwrap_or_default();
    let paths = paths
        .iter()
        .filter_map(|s| Pattern::new(s).ok())
        .collect::<Vec<_>>();

    let walk = hpk::walk(input)?;

    fn matches_path(path: &Path, paths: &[Pattern]) -> bool {
        if paths.is_empty() {
            return true;
        }
        for p in paths {
            if p.matches_path(path) {
                return true;
            }
        }
        false
    }

    for dent in walk.flatten() {
        if !matches_path(dent.path(), &paths) {
            continue;
        }
        if !dent.is_dir() {
            println!("{}", dent.path().display());
        }
    }
    Ok(())
}