use clap::{App, Arg}; pub fn app() -> App<'static> { App::new(env!("CARGO_PKG_NAME")) .version(env!("CARGO_PKG_VERSION")) .about(env!("CARGO_PKG_DESCRIPTION")) .subcommand(average()) .subcommand(cat()) .subcommand(delta()) .subcommand(print()) .subcommand(info()) .subcommand(colorburst()) .subcommand(transpose()) .arg(precision()) .arg(output_file()) .arg(files().required(false)) } fn average() -> App<'static> { App::new("average") .about("Average two or more CGATS files") .alias("avg") .arg(reindex()) .arg(output_file()) .arg(files()) } fn cat() -> App<'static> { App::new("concatenate") .about("Concatenate rows from multiple CGATS files") .alias("cat") .arg(output_file()) .arg(files()) } fn delta() -> App<'static> { App::new("delta") .about("Calculate DeltaE between all values in two compatible CGATS files") .arg( Arg::new("method") .help("DE method") .long("method") .short('m') .takes_value(true) .possible_values(["1976", "1994G", "1994T", "2000", "CMC", "CMC2"]) .ignore_case(true) .default_value("2000") ) .arg( Arg::new("report") .help("Generate a DE Report message") .long("report") .short('r') .takes_value(false) ) .arg( Arg::new("percentile") .help("Percentile (0-100) to use in best/worst analysis") .long("percentile") .aliases(&["percent", "pct"]) .value_name("PCT%") .short('p') .takes_value(true) .multiple_values(false) .default_value("95") .validator(is_pct) ) .arg(output_file()) .arg(files() .help("CGATS files to compare. Reference must come before sample.") .number_of_values(2) .value_names(&["REFERENCE", "SAMPLE"]) ) } fn print() -> App<'static> { App::new("print") .about("Print the processed CGATS file(s)") .arg(reindex()) .arg(output_file()) .arg(files()) } fn info() -> App<'static> { App::new("info") .about("Print basic CGATS info") .arg(output_file()) .arg(files()) } fn colorburst() -> App<'static> { App::new("colorburst") .about("Convert to and from ColorBurst linearization format") .arg( Arg::new("to-cgats") .help("Convert ColorBurst linearization to regular CGATS") .long("to-cgats") .alias("cgats") .takes_value(false) ) .arg(reindex()) .arg(output_file()) .arg(files()) } fn transpose() -> App<'static> { App::new("transpose") .about("Transpose chart patches given a layout width") .arg( Arg::new("width") .help("Specify the current chart width in number of patches") .long("width") .short('w') .takes_value(true) ) .arg(reindex()) .arg(output_file()) .arg(files().number_of_values(1)) } fn precision() -> Arg<'static> { Arg::new("precision") .help("Number of decimal places to round values to") .long("precision") .short('P') .alias("places") .takes_value(true) .validator(is_int) .default_value("4") } fn output_file() -> Arg<'static> { Arg::new("output-file") .help("Write output to a file") .long("output-file") .aliases(&["file", "output"]) .short('f') .takes_value(true) .max_values(1) } fn reindex() -> Arg<'static> { Arg::new("reindex") .help("Re-index the SAMPLE_ID field starting at 1") .long("reindex") .short('R') .takes_value(false) } fn files() -> Arg<'static> { Arg::new("files") .takes_value(true) .multiple_values(true) .value_name("FILE") .help("CGATS files") .required(true) } fn is_pct(arg: &str) -> std::result::Result<(), String> { match arg.parse::() { Ok(f) => { if f > 0.0 && f < 100.0 { Ok(()) } else { Err("value must be greater than 0 and less than 100".into()) } } Err(e) => Err(e.to_string()) } } fn is_int(arg: &str) -> std::result::Result<(), String> { match arg.parse::() { Ok(_) => Ok(()), Err(e) => Err(format!("value must be an integer: {e}")), } }