use allow_core::{CargoAllowError, CargoAllowResult};
use clap::{CommandFactory, Parser, Subcommand, ValueEnum};
use std::env;
use crate::{
add, audit, check, diff, doctor, explain, init, list, migrate, propose, prune, refresh, why,
worklist,
};
#[derive(Debug, Parser)]
#[command(
name = "cargo-allow",
about = "Source exception ledger for source trees",
version
)]
pub(crate) struct CargoAllowCli {
#[arg(long, value_enum, default_value_t = ColorChoice::Auto)]
pub(crate) color: ColorChoice,
#[command(subcommand)]
pub(crate) command: Option<CargoAllowCommand>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub(crate) enum ColorChoice {
Auto,
Always,
Never,
}
#[derive(Debug, Subcommand)]
pub(crate) enum CargoAllowCommand {
Init(init::InitArgs),
Audit(audit::ReportArgs),
Check(check::CheckArgs),
Diff(diff::DiffArgs),
List(list::ListArgs),
Explain(explain::ExplainArgs),
Why(why::WhyArgs),
Add(add::AddArgs),
Propose(propose::ProposeArgs),
Worklist(worklist::WorklistArgs),
Migrate(migrate::MigrateArgs),
Refresh(refresh::RefreshArgs),
Prune(prune::PruneArgs),
Doctor(doctor::DoctorArgs),
}
pub(crate) fn run() -> CargoAllowResult<()> {
let cli = CargoAllowCli::parse_from(normalized_args(env::args()));
let _color = cli.color;
let Some(command) = cli.command else {
CargoAllowCli::command()
.print_help()
.map_err(|e| CargoAllowError::new(format!("failed to print help: {e}")))?;
println!();
return Ok(());
};
match command {
CargoAllowCommand::Init(args) => init::cmd_init(&args),
CargoAllowCommand::Audit(args) => audit::cmd_audit(&args),
CargoAllowCommand::Check(args) => check::cmd_check(&args),
CargoAllowCommand::Diff(args) => diff::cmd_diff(&args),
CargoAllowCommand::List(args) => list::cmd_list(&args),
CargoAllowCommand::Explain(args) => explain::cmd_explain(&args),
CargoAllowCommand::Why(args) => why::cmd_why(&args),
CargoAllowCommand::Add(args) => add::cmd_add(&args),
CargoAllowCommand::Propose(args) => propose::cmd_propose(&args),
CargoAllowCommand::Worklist(args) => worklist::cmd_worklist(&args),
CargoAllowCommand::Migrate(args) => migrate::cmd_migrate(&args),
CargoAllowCommand::Refresh(args) => refresh::cmd_refresh(&args),
CargoAllowCommand::Prune(args) => prune::cmd_prune(&args),
CargoAllowCommand::Doctor(args) => doctor::cmd_doctor(&args),
}
}
pub(crate) fn normalized_args(args: impl IntoIterator<Item = String>) -> Vec<String> {
let mut args = args.into_iter().collect::<Vec<_>>();
if let Some(index) = leading_cargo_allow_shim_index(&args) {
args.remove(index);
}
args
}
fn leading_cargo_allow_shim_index(args: &[String]) -> Option<usize> {
for (index, arg) in args.iter().enumerate().skip(1) {
if arg == "allow" {
return if should_strip_cargo_allow_shim(args, index + 1) {
Some(index)
} else {
None
};
}
if is_known_subcommand(arg) {
return None;
}
}
None
}
fn should_strip_cargo_allow_shim(args: &[String], start: usize) -> bool {
let mut saw_token = false;
for arg in args.iter().skip(start) {
saw_token = true;
if is_known_subcommand(arg) {
return true;
}
if arg.starts_with('-') {
continue;
}
return false;
}
saw_token
}
fn is_known_subcommand(arg: &str) -> bool {
CargoAllowCommand::SUBCOMMANDS.contains(&arg)
}
impl CargoAllowCommand {
pub(crate) const SUBCOMMANDS: &[&str] = &[
"init", "audit", "check", "diff", "list", "explain", "why", "add", "propose", "worklist",
"migrate", "refresh", "prune", "doctor",
];
}