meansd-cli 1.3.0

calculate mean and standard deviation (CLI)
use std::num::NonZeroU32;

use clap::ArgMatches;
use clap::{arg_enum, value_t};

arg_enum! {
    #[derive(Clone, Copy, Debug, PartialEq)]
    pub enum ErrorHandler {
        Panic,
        Skip,
        Warn,
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum StandardDeviationMode {
    Population,
    Sample,
}

#[derive(Clone, Debug, PartialEq)]
pub struct Config {
    pub error_handler: ErrorHandler,
    pub progress: Option<f64>,
    pub bin_width: Option<NonZeroU32>,
    pub bin_breaks: Option<Vec<f64>>,
    pub sd_mode: StandardDeviationMode,
}

impl Config {
    pub fn from_args(args: &ArgMatches) -> Self {
        let error_handler = value_t!(args, "handler", ErrorHandler);
        let error_handler = error_handler.unwrap_or_else(|e| e.exit());

        let progress = args.value_of("progress");
        let progress = progress.map(|s| s.parse::<f64>().unwrap());

        let bin_width = args.value_of("bin_width");
        let bin_width = bin_width.map(|s| s.parse::<NonZeroU32>().unwrap());

        let bin_breaks = args.values_of("bin_breaks");
        let bin_breaks = bin_breaks.map(|values| {
            // safe to unwrap because of clap validator
            values.map(|value| value.parse::<f64>().unwrap()).collect()
        });

        let population = args.is_present("population");
        let sample = args.is_present("sample");

        let sd_mode = match (population, sample) {
            (true, false) => StandardDeviationMode::Population,
            (false, _) => StandardDeviationMode::Sample,
            (true, true) => unreachable!("flags override each other"),
        };

        Self {
            progress,
            error_handler,
            bin_width,
            bin_breaks,
            sd_mode,
        }
    }
}