gitall 0.6.0

Executes git(1) commands in repos below a parent directory
Documentation
use clap::{
    crate_authors, crate_description, crate_version, value_t_or_exit, App, Arg, ArgMatches,
};

use std::ffi::OsStr;
use std::fmt;
use std::str::FromStr;

pub enum ColorMode {
    Always,
    Auto,
    Never,
}

pub struct ColorModeError {
    kind: ColorModeErrorKind,
}
enum ColorModeErrorKind {
    NoMatch,
}

impl ColorMode {
    pub fn variants() -> Vec<&'static str> {
        vec!["always", "true", "auto", "never", "false"]
    }
}

impl fmt::Display for ColorMode {
    fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
        let text = match self {
            ColorMode::Always => "always",
            ColorMode::Auto => "auto",
            ColorMode::Never => "never",
        };
        write!(f, "{}", text)
    }
}

impl FromStr for ColorMode {
    type Err = ColorModeError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.eq_ignore_ascii_case("always") {
            Ok(ColorMode::Always)
        } else if s.eq_ignore_ascii_case("auto") || s.eq_ignore_ascii_case("true") {
            Ok(ColorMode::Auto)
        } else if s.eq_ignore_ascii_case("never") || s.eq_ignore_ascii_case("false") {
            Ok(ColorMode::Never)
        } else {
            Err(ColorModeError {
                kind: ColorModeErrorKind::NoMatch,
            })
        }
    }
}

pub fn build_cli() -> App<'static, 'static> {
    App::new("gitall")
        .version(crate_version!())
        .author(crate_authors!(", "))
        .about(crate_description!())
        .arg(Arg::with_name("follow_links")
             .short("L")
             .long("follow")
             .help("Follow symbolic links")
             .long_help("When specified, symbolic links will be followed when navigating the directory tree."))
        .arg(Arg::with_name("color_mode")
             .long("color")
             .takes_value(true)
             .possible_values(&ColorMode::variants())
             .case_insensitive(true)
             .value_name("WHEN")
             .default_value("auto")
             .help("Controls when to use color"))
        .arg(Arg::with_name("dir")
             .short("D")
             .long("directory")
             .help("The directory to start searching under")
             .takes_value(true)
             .value_name("DIR")
             .default_value("."))
        .arg(Arg::with_name("max_depth")
             .short("d")
             .long("max-depth")
             .help("Descend at most LEVELS of directories below DIR")
             .takes_value(true)
             .value_name("LEVELS"))
        .arg(Arg::with_name("full_path")
            .long("full-path")
            .help("Match REGEX against the full directory path")
            .long_help("By default, REGEX matches against only the directory name. Using this flag, REGEX matches against the full canonical path."))
        .arg(Arg::with_name("regex")
            .short("r")
            .long("regex")
            .help("Filters command to repo(s) matching provided regular expression")
            .takes_value(true)
            .value_name("REGEX")
            .default_value(".*"))
        .arg(Arg::with_name("workers")
            .short("j")
            .long("threads")
            .help("The maximum number of commands to run in parallel")
            .takes_value(true)
            .value_name("NUM"))
        .arg(Arg::with_name("executable")
            .short("X")
            .long("executable")
            .help("The program to run in each repo")
            .takes_value(true)
            .value_name("PROGRAM")
            .default_value_os(OsStr::new("git")))
        .arg(Arg::with_name("cmd")
             .help("A single git command to run in each repo")
             .index(1)
             .required(true)
             .empty_values(false)
             .multiple(true)
             .value_name("COMMAND"))
}

pub fn get_color_mode(matches: &ArgMatches) -> ColorMode {
    value_t_or_exit!(matches, "color_mode", ColorMode)
}