use clap::{Parser, ValueEnum};
use std::path::PathBuf;
use crate::finding::Severity;
#[derive(Debug, Parser)]
#[command(name = "cargo", bin_name = "cargo")]
pub enum Cargo {
BuildRx(Args),
}
#[derive(Debug, Parser)]
#[command(version, about = "Compile-time diagnostic and prescription tool")]
pub struct Args {
#[arg(long, default_value = "terminal")]
pub format: Format,
#[arg(long, default_value = "auto")]
pub color: ColorChoice,
#[arg(long, value_delimiter = ',')]
pub only: Vec<String>,
#[arg(long, value_delimiter = ',')]
pub skip: Vec<String>,
#[arg(long, default_value = "info")]
pub min_severity: SeverityFilter,
#[arg(long, default_value = "none")]
pub deny: DenyLevel,
#[arg(long)]
pub manifest_path: Option<PathBuf>,
}
#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum Format {
Terminal,
Json,
}
#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum ColorChoice {
Auto,
Always,
Never,
}
#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum SeverityFilter {
Fix,
Warn,
Info,
}
impl SeverityFilter {
#[must_use]
pub fn passes(self, severity: Severity) -> bool {
match self {
SeverityFilter::Info => true,
SeverityFilter::Warn => matches!(severity, Severity::Fix | Severity::Warn),
SeverityFilter::Fix => matches!(severity, Severity::Fix),
}
}
}
#[derive(Debug, Clone, Copy, ValueEnum, PartialEq, Eq)]
pub enum DenyLevel {
None,
Fix,
Warn,
Info,
}
impl DenyLevel {
#[must_use]
pub fn triggers(self, severity: Severity) -> bool {
match self {
DenyLevel::None => false,
DenyLevel::Fix => severity == Severity::Fix,
DenyLevel::Warn => matches!(severity, Severity::Fix | Severity::Warn),
DenyLevel::Info => true,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const ALL: [Severity; 3] = [Severity::Fix, Severity::Warn, Severity::Info];
#[test]
fn severity_filter_is_monotone() {
for s in ALL {
if SeverityFilter::Fix.passes(s) {
assert!(SeverityFilter::Warn.passes(s));
}
if SeverityFilter::Warn.passes(s) {
assert!(SeverityFilter::Info.passes(s));
}
}
}
#[test]
fn info_filter_passes_everything() {
assert!(ALL.into_iter().all(|s| SeverityFilter::Info.passes(s)));
}
#[test]
fn deny_none_never_triggers() {
assert!(ALL.into_iter().all(|s| !DenyLevel::None.triggers(s)));
}
#[test]
fn deny_is_monotone_with_severity() {
for s in ALL {
if DenyLevel::Fix.triggers(s) {
assert!(DenyLevel::Warn.triggers(s));
}
if DenyLevel::Warn.triggers(s) {
assert!(DenyLevel::Info.triggers(s));
}
}
}
}