vsec 0.0.1

Detect secrets and in Rust codebases
Documentation
// src/cli/args.rs

use std::path::PathBuf;

use clap::{Args, Parser, Subcommand, ValueEnum};

#[derive(Parser)]
#[command(
    name = "secretrace",
    author = "Secretrace Contributors",
    version,
    about = "Detect hardcoded secrets in Rust codebases",
    long_about = "A research-grade static analysis tool for detecting hardcoded secrets \
                  in Rust code with intelligent false-positive filtering."
)]
pub struct Cli {
    #[command(subcommand)]
    pub command: Commands,

    /// Enable verbose output
    #[arg(short, long, global = true)]
    pub verbose: bool,

    /// Suppress all output except errors
    #[arg(short, long, global = true)]
    pub quiet: bool,

    /// Path to configuration file
    #[arg(short, long, global = true)]
    pub config: Option<PathBuf>,
}

#[derive(Subcommand)]
pub enum Commands {
    /// Scan a directory or file for secrets
    Scan(ScanArgs),

    /// Initialize a configuration file
    Init(InitArgs),

    /// Explain a specific finding
    Explain(ExplainArgs),

    /// Show scoring details for a file
    Debug(DebugArgs),
}

#[derive(Args)]
pub struct ScanArgs {
    /// Path to scan (file or directory)
    #[arg(default_value = ".")]
    pub path: PathBuf,

    /// Minimum score threshold for reporting
    #[arg(short, long, default_value = "70")]
    pub threshold: i32,

    /// Output format
    #[arg(short, long, value_enum, default_value = "text")]
    pub format: OutputFormatArg,

    /// Output file (stdout if not specified)
    #[arg(short, long)]
    pub output: Option<PathBuf>,

    /// Include test files in scan
    #[arg(long)]
    pub include_tests: bool,

    /// Include examples in scan
    #[arg(long)]
    pub include_examples: bool,

    /// Paths to ignore (glob patterns)
    #[arg(long)]
    pub ignore: Vec<String>,

    /// Only scan these paths (glob patterns)
    #[arg(long)]
    pub only: Vec<String>,

    /// Maximum number of files to scan (0 for unlimited)
    #[arg(long, default_value = "0")]
    pub max_files: usize,

    /// Number of parallel workers
    #[arg(short = 'j', long)]
    pub jobs: Option<usize>,

    /// Exit with non-zero status if findings are found
    #[arg(long)]
    pub fail_on_findings: bool,

    /// Show score breakdown for each finding
    #[arg(long)]
    pub show_scores: bool,

    // Git History Scanning Options

    /// Scan git history for secrets in deleted/modified files
    #[arg(long)]
    pub scan_history: bool,

    /// Maximum number of commits to scan (0 for unlimited)
    #[arg(long, default_value = "0")]
    pub max_commits: usize,

    /// Only scan commits after this date (YYYY-MM-DD)
    #[arg(long)]
    pub since: Option<String>,

    /// Only scan commits before this date (YYYY-MM-DD)
    #[arg(long)]
    pub until: Option<String>,

    /// Branch to scan (default: current branch)
    #[arg(long)]
    pub branch: Option<String>,

    /// Exclude deleted files from git history scan (only show secrets in current files)
    #[arg(long)]
    pub exclude_deleted: bool,

    /// Only show secrets from deleted files (files that no longer exist)
    #[arg(long)]
    pub only_deleted: bool,
}

#[derive(Args)]
pub struct InitArgs {
    /// Output path for configuration file
    #[arg(short, long, default_value = ".vsec.toml")]
    pub output: PathBuf,

    /// Overwrite existing file
    #[arg(short, long)]
    pub force: bool,

    /// Create a minimal configuration
    #[arg(long)]
    pub minimal: bool,
}

#[derive(Args)]
pub struct ExplainArgs {
    /// Finding ID to explain
    pub finding_id: String,

    /// Path to scan results file
    #[arg(short, long)]
    pub results: Option<PathBuf>,
}

#[derive(Args)]
pub struct DebugArgs {
    /// Path to file to debug
    pub path: PathBuf,

    /// Show all findings (even killed ones)
    #[arg(long)]
    pub show_all: bool,

    /// Show AST dump
    #[arg(long)]
    pub show_ast: bool,
}

#[derive(Clone, Copy, ValueEnum)]
pub enum OutputFormatArg {
    /// Human-readable text
    Text,
    /// JSON output
    Json,
    /// SARIF format (for CI integration)
    Sarif,
    /// Markdown report
    Markdown,
    /// GitHub Actions annotations
    Github,
}

impl From<OutputFormatArg> for crate::output::OutputFormat {
    fn from(arg: OutputFormatArg) -> Self {
        match arg {
            OutputFormatArg::Text => Self::Text,
            OutputFormatArg::Json => Self::Json,
            OutputFormatArg::Sarif => Self::Sarif,
            OutputFormatArg::Markdown => Self::Markdown,
            OutputFormatArg::Github => Self::Github,
        }
    }
}