use std::io::IsTerminal;
use std::path::PathBuf;
use clap::Parser;
#[derive(Parser, Debug)]
#[command(name = "git-barber", bin_name = "git barber", version, about)]
pub struct Cli {
#[arg(long, value_name = "REF")]
pub base: Option<String>,
#[arg(long, visible_alias = "dry-run", conflicts_with = "yes")]
pub list: bool,
#[arg(long)]
pub json: bool,
#[arg(long)]
pub yes: bool,
#[arg(long)]
pub remote: bool,
#[arg(long, requires = "yes")]
pub include_gone: bool,
#[arg(long, value_name = "PATTERN", value_delimiter = ',')]
pub protect: Vec<String>,
#[arg(long)]
pub fetch: bool,
#[arg(short = 'C', value_name = "PATH")]
pub dir: Option<PathBuf>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Mode {
Tui,
List,
Execute,
}
impl Cli {
pub fn mode(&self) -> Mode {
let tty = std::io::stdin().is_terminal() && std::io::stdout().is_terminal();
self.mode_with(tty)
}
fn mode_with(&self, tty: bool) -> Mode {
if self.yes {
Mode::Execute
} else if self.list || self.json || !tty {
Mode::List
} else {
Mode::Tui
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn parse(args: &[&str]) -> Cli {
Cli::try_parse_from(std::iter::once("git-barber").chain(args.iter().copied())).unwrap()
}
#[test]
fn verify_cli() {
use clap::CommandFactory;
Cli::command().debug_assert();
}
#[test]
fn default_is_tui_in_tty_and_list_otherwise() {
let cli = parse(&[]);
assert_eq!(cli.mode_with(true), Mode::Tui);
assert_eq!(cli.mode_with(false), Mode::List);
}
#[test]
fn dry_run_and_delete_flags_conflict() {
assert!(Cli::try_parse_from(["git-barber", "--yes", "--list"]).is_err());
assert!(Cli::try_parse_from(["git-barber", "--yes", "--dry-run"]).is_err());
let cli = parse(&["--yes", "--json"]);
assert_eq!(cli.mode_with(true), Mode::Execute);
}
#[test]
fn include_gone_requires_yes() {
assert!(Cli::try_parse_from(["git-barber", "--include-gone"]).is_err());
assert!(Cli::try_parse_from(["git-barber", "--yes", "--include-gone"]).is_ok());
}
#[test]
fn json_and_list_force_listing() {
assert_eq!(parse(&["--json"]).mode_with(true), Mode::List);
assert_eq!(parse(&["--list"]).mode_with(true), Mode::List);
assert_eq!(parse(&["--dry-run"]).mode_with(true), Mode::List);
}
#[test]
fn protect_accepts_commas_and_repeats() {
let cli = parse(&["--protect", "release/*,staging", "--protect", "qa"]);
assert_eq!(cli.protect, vec!["release/*", "staging", "qa"]);
}
}