use crate::output::Format;
use clap::{Args, Parser, Subcommand};
use std::path::PathBuf;
#[derive(Parser)]
#[command(name = "unsafe-budget")]
#[command(about = "keeps the unsafety demons out. an unsafe code budget gate for CI pipelines.")]
#[command(version)]
pub struct Cli {
#[command(subcommand)]
pub command: Command,
}
#[derive(Subcommand)]
pub enum Command {
Scan(ScanArgs),
Check(ScanArgs),
Update(ScanArgs),
Plugins(PluginsArgs),
}
#[derive(Args, Clone)]
pub struct ScanArgs {
#[arg(long, default_value = "text")]
pub format: Format,
#[arg(long, default_value = "auto")]
pub analyzer: String,
#[arg(long)]
pub workspace_only: bool,
#[arg(long, conflicts_with = "no_deps")]
pub include_deps: bool,
#[arg(long)]
pub no_deps: bool,
#[arg(long, value_delimiter = ',')]
pub features: Vec<String>,
#[arg(long)]
pub all_features: bool,
#[arg(long)]
pub no_default_features: bool,
#[arg(long)]
pub all_targets: bool,
#[arg(long, value_delimiter = ',')]
pub targets: Vec<String>,
#[arg(long)]
pub manifest_path: Option<PathBuf>,
#[arg(long)]
pub config: Option<PathBuf>,
#[arg(long)]
pub details: bool,
#[arg(long)]
pub plugin_timeout: Option<u64>,
}
#[derive(Args)]
pub struct PluginsArgs {
#[arg(long, default_value = "text")]
pub format: Format,
}
pub fn parse() -> Cli {
let mut args: Vec<String> = std::env::args().collect();
if args.len() > 1 && args[1] == "unsafe-budget" {
args.remove(1);
}
Cli::parse_from(args)
}
#[cfg(test)]
mod tests {
use super::*;
use clap::error::ErrorKind;
#[test]
fn include_deps_and_no_deps_conflict() {
let result = Cli::try_parse_from(["unsafe-budget", "scan", "--include-deps", "--no-deps"]);
match result {
Err(e) => assert_eq!(e.kind(), ErrorKind::ArgumentConflict),
Ok(_) => panic!("expected ArgumentConflict error"),
}
}
#[test]
fn include_deps_alone_is_accepted() {
let cli = Cli::try_parse_from(["unsafe-budget", "scan", "--include-deps"]).unwrap();
match cli.command {
Command::Scan(args) => {
assert!(args.include_deps);
assert!(!args.no_deps);
}
_ => panic!("expected scan command"),
}
}
#[test]
fn no_deps_alone_is_accepted() {
let cli = Cli::try_parse_from(["unsafe-budget", "scan", "--no-deps"]).unwrap();
match cli.command {
Command::Scan(args) => {
assert!(!args.include_deps);
assert!(args.no_deps);
}
_ => panic!("expected scan command"),
}
}
}