const VERSION: &str = env!("CARGO_PKG_VERSION");
const ARGS: &str = "v?aA";
fn do_help() {
println!("{} [-v | -? ] | [ -a | -A ] <number> ...", NAME);
println!("");
println!("Find and print the {} value from a list of provided numbers.", ACTION);
println!("");
println!("Options");
println!(" -v Print program version and exit.");
println!(" -? Show this help message.");
println!(" -a Compare numbers by their absolute value instead of their raw value.");
println!(" -A Print absolute value of returned number. Implies -a.");
println!("");
println!("Exit Codes");
println!(" 0 Success");
println!(" 1 No numeric arguments provided");
println!(" 2 One or more numeric arguments failed to parse or were invalid");
println!("");
println!("This is free and unencumbered software released into the public domain.");
}
pub fn index_of_<F>(numbers: &[f64], mut cmp: F) -> usize
where
F: FnMut(&f64, &f64) -> std::cmp::Ordering,
{
numbers
.iter()
.enumerate()
.filter(|(_, &v)| v.is_finite()) .min_by(|(_, a), (_, b)| cmp(a, b))
.expect("All values are NaN or Infinite")
.0
}
#[cfg(test)]
mod indexmin_tests;
pub fn args_as_floats(g: &getopt2::getopt, numbers: &mut Vec<f64>) -> bool {
numbers.clear();
let mut parse_fail = false;
for n_str in g.iter() {
debug_assert!({
println!("parsing n_str='{}'", n_str);
true
});
match n_str.parse::<f64>() {
Ok(num) => {
if num.is_nan() {
eprintln!("Error parsing '{}': NaN values are not allowed.", n_str);
parse_fail = true;
} else if num.is_infinite() {
eprintln!("Error parsing '{}': Infinite values are not allowed.", n_str);
parse_fail = true;
} else {
numbers.push(num);
}
}
Err(e) => {
eprintln!("Error parsing '{}': {}", n_str, e);
parse_fail = true
}
}
}
!parse_fail
}
fn to_comparable_numbers(numbers: &[f64], use_abs: bool) -> Vec<f64> {
let mut comparable_numbers = Vec::<f64>::with_capacity(numbers.len());
for &n in numbers.iter() {
if use_abs {
comparable_numbers.push(n.abs());
} else {
comparable_numbers.push(n);
}
}
comparable_numbers
}
use getopt2::hideBin;
use std::process::ExitCode;
fn main() -> ExitCode {
let g = getopt2::new(hideBin(std::env::args()), ARGS).unwrap();
if g.has('v') {
println!("{}", VERSION);
ExitCode::SUCCESS
} else if g.has('?') {
do_help();
ExitCode::SUCCESS
} else if g.len() == 0 {
do_help();
ExitCode::from(1)
} else {
let use_abs = g.has('a') || g.has('A');
let mut numbers: Vec<f64> = Vec::with_capacity(g.len());
if !args_as_floats(&g, &mut numbers) {
ExitCode::from(2)
} else {
let comparable_numbers: Vec<f64> = to_comparable_numbers(&numbers, use_abs);
let i = index_of_(&comparable_numbers, num_cmp());
if g.has('A') {
numbers.iter_mut().for_each(|x| *x = x.abs());
}
println!("{}", numbers[i]);
ExitCode::SUCCESS
}
}
}