mod args; mod zip; use std::fs::File; use std::io; use std::process::ExitCode; fn guess_parser(args: &mut dyn Iterator) -> Box { for arg in args { if arg.len() > 2 && arg.starts_with("--") { // --C.. can only be ShortAndLong return Box::new(args::ShortAndLongParser::new()); } } // TODO: Can we make more guesses? Box::new(args::LongOnlyParser::new()) } struct Program { classname: String, args: Vec, classpath: String, verbose: bool, } fn parse_args() -> Result, String> { let mut options = args::Options::new(); let help_idx = options.push( args::OptionBuilder::default() .short('h') .long("help") .description("display this and exit") .build() .unwrap(), ); let version_idx = options.push( args::OptionBuilder::default() .short('V') .long("version") .description("display version and exit") .build() .unwrap(), ); let verbose_idx = options.push( args::OptionBuilder::default() .long("verbose") .description("be verbose") .build() .unwrap(), ); let cp_idx = options.push( args::OptionBuilder::default() .long("cp") .description("class search path of directories and zip/jar files") .value(args::ValueRequirement::Required("CLASSPATH")) .build() .unwrap(), ); let classpath_idx = options.push( args::OptionBuilder::default() .long("classpath") .description("class search path of directories and zip/jar files") .value(args::ValueRequirement::Required("CLASSPATH")) .build() .unwrap(), ); let parser = guess_parser(&mut std::env::args()); let maybe_args = parser.run(&mut options, &mut std::env::args()); // help is special, check for it even if parser#run returned error. let help = &options[help_idx]; if help.is_set() { parser.print_help(&options); return Ok(None); } let args = maybe_args?; let version = &options[version_idx]; if version.is_set() { println!("Version is 0.0.1"); return Ok(None); } let verbose = &options[verbose_idx]; let cp = &options[cp_idx]; let classpath = &options[classpath_idx]; let actual_classpath: &str; if cp.is_set() { if classpath.is_set() { return Err("Both -cp and -classpath set, pick one.".to_string()); } actual_classpath = cp.value().as_ref().unwrap(); } else if classpath.is_set() { actual_classpath = classpath.value().as_ref().unwrap(); } else { actual_classpath = ""; } let mut run_args = args.args.iter(); if let Some(classname) = run_args.next() { Ok(Some(Program { classname: classname.clone(), args: run_args.map(|s| s.clone()).collect(), classpath: actual_classpath.to_string(), verbose: verbose.is_set(), })) } else { Err("Missing class name".to_string()) } } fn run(program: Program) -> io::Result { let mut paths = program.classpath.split(':'); let mut file = File::open(paths.next().unwrap())?; let layout = zip::Layout::new(&mut file)?; for (name, idx) in layout.names() { let entry = &layout.entries()[*idx]; println!( "{name}: {} {}", entry.compressed_size(), entry.uncompressed_size() ); } return Ok(ExitCode::SUCCESS); } fn main() -> ExitCode { match parse_args() { Ok(maybe_program) => match maybe_program { Some(program) => match run(program) { Ok(exit_code) => { return exit_code; } Err(err) => { eprintln!("{}", err); return ExitCode::FAILURE; } }, None => { return ExitCode::SUCCESS; } }, Err(msg) => { eprintln!("{}", msg); return ExitCode::FAILURE; } } }