retrocompressor 1.0.1

file compression with retro formats
Documentation
use clap::{arg,crate_version,Command};
use retrocompressor::{lzw,lzss_huff, td0, direct_ports};
type STDRESULT = Result<(),Box<dyn std::error::Error>>;

const RCH: &str = "unreachable was reached";

fn ok_to_overwrite(path_out: &str) -> bool {
    if let Ok(_f) = std::fs::File::open(path_out) {
        let mut ans = String::new();
        eprint!("{} exists, overwrite? (y/n) ",path_out);
        std::io::stdin().read_line(&mut ans).expect("could not read stdin");
        if ans.trim_end()=="y" || ans.trim_end()=="Y" {
            log::warn!("existing file will not be truncated");
            return true;
        }
        return false;
    }
    true
}

fn main() -> STDRESULT
{
    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init();
    let long_help =
"Examples:
---------
Compress:      `retrocompressor compress -m lzss_huff -i my_compressed -o my_expanded`
Expand:        `retrocompressor expand -m lzss_huff -i my_expanded -o my_compressed`";

    let methods = ["lzw","lzhuf-port","lzss_huff","td0"];

    let mut main_cmd = Command::new("retrocompressor")
        .about("Compress and expand with retro formats")
        .after_long_help(long_help)
        .version(crate_version!());
    main_cmd = main_cmd.subcommand(Command::new("compress")
        .arg(arg!(-m --method <METHOD> "compression algorithm").value_parser(methods)
            .required(true))
        .arg(arg!(-i --input <PATH> "input path").required(true))
        .arg(arg!(-o --output <PATH> "output path").required(true))
        .about("compress a file"));

        main_cmd = main_cmd.subcommand(Command::new("expand")
        .arg(arg!(-m --method <METHOD> "compression algorithm").required(true))
        .arg(arg!(-i --input <PATH> "input path").required(true))
        .arg(arg!(-o --output <PATH> "output path").required(true))
        .about("expand a file"));

    let matches = main_cmd.get_matches();
    
    if let Some(cmd) = matches.subcommand_matches("compress") {
        let path_in = cmd.get_one::<String>("input").expect(RCH);
        let path_out = cmd.get_one::<String>("output").expect(RCH);
        let method = cmd.get_one::<String>("method").expect(RCH);
        if !ok_to_overwrite(path_out) {
            eprintln!("abort operation");
            return Ok(());
        }
        let mut in_file = std::fs::File::open(path_in)?;
        let mut out_file = std::fs::OpenOptions::new().write(true).truncate(false).create(true).open(path_out)?;
        let (in_size,out_size) = match method.as_str() {
            "lzw" => lzw::compress(&mut in_file,&mut out_file,&lzw::STD_OPTIONS)?,
            "lzhuf-port" => direct_ports::lzhuf::encode(&mut in_file,&mut out_file)?,
            "lzss_huff" => lzss_huff::compress(&mut in_file,&mut out_file,&lzss_huff::STD_OPTIONS)?,
            "td0" => td0::compress(&mut in_file,&mut out_file)?,
            _ => {
                eprintln!("{} not supported",method);
                return Err(Box::new(std::fmt::Error));
            }
        };
        out_file.set_len(out_size)?;
        eprintln!("compressed {} into {}",in_size,out_size);
    }

    if let Some(cmd) = matches.subcommand_matches("expand") {
        let path_in = cmd.get_one::<String>("input").expect(RCH);
        let path_out = cmd.get_one::<String>("output").expect(RCH);
        let method = cmd.get_one::<String>("method").expect(RCH);
        if !ok_to_overwrite(path_out) {
            eprintln!("abort operation");
            return Ok(());
        }
        let mut in_file = std::fs::File::open(path_in)?;
        let mut out_file = std::fs::OpenOptions::new().write(true).truncate(false).create(true).open(path_out)?;
        let (in_size,out_size) = match method.as_str() {
            "lzw" => lzw::expand(&mut in_file,&mut out_file,&lzw::STD_OPTIONS)?,
            "lzhuf-port" => direct_ports::lzhuf::decode(&mut in_file,&mut out_file)?,
            "lzss_huff" => lzss_huff::expand(&mut in_file,&mut out_file,&lzss_huff::STD_OPTIONS)?,
            "td0" => td0::expand(&mut in_file,&mut out_file)?,
            _ => {
                eprintln!("{} not supported",method);
                return Err(Box::new(std::fmt::Error));
            }
        };
        out_file.set_len(out_size)?;
        eprintln!("expanded {} into {}",in_size,out_size);
    }

    Ok(())   
}