cargo-sonar 1.6.0

Helper to transform reports from Rust tooling for code quality, into valid Sonar report
Documentation
use clap::CommandFactory;
use clap_complete::{generator::generate, shells};
use std::path::PathBuf;

#[cfg(any(
    feature = "audit",
    feature = "clippy",
    feature = "deny",
    feature = "outdated",
    feature = "typos",
    feature = "udeps"
))]
#[derive(Debug, Clone, Copy, PartialEq, clap::ValueEnum, strum::EnumString)]
#[non_exhaustive]
pub enum Issue {
    #[cfg(feature = "audit")]
    #[strum(serialize = "audit")]
    Audit,
    #[cfg(feature = "clippy")]
    #[strum(serialize = "clippy")]
    Clippy,
    #[cfg(feature = "deny")]
    #[strum(serialize = "deny")]
    Deny,
    #[cfg(feature = "outdated")]
    #[strum(serialize = "outdated")]
    Outdated,
    #[cfg(feature = "typos")]
    #[strum(serialize = "typos")]
    Typos,
    #[cfg(feature = "udeps")]
    #[strum(serialize = "udeps")]
    Udeps,
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, clap::ValueEnum)]
#[non_exhaustive]
// ALLOW: 'SHell' is incorrectly detected as code that must be wrapped into backticks
#[allow(clippy::doc_markdown)]
pub enum Shell {
    /// Bourne Again SHell (bash)
    Bash,
    /// Elvish shell
    Elvish,
    /// Friendly Interactive SHell (fish)
    Fish,
    /// Nushell
    Nushell,
    /// PowerShell
    // ALLOW: 'PowerShell' is the name that does ends with 'Shell'
    #[allow(clippy::enum_variant_names)]
    PowerShell,
    /// Z SHell (zsh)
    Zsh,
}

#[derive(Debug, Clone)]
pub enum InputPath {
    Stdin,
    File(PathBuf),
}

impl clap::builder::ValueParserFactory for InputPath {
    type Parser = InputPathValueParser;

    fn value_parser() -> Self::Parser {
        InputPathValueParser
    }
}

#[derive(Debug, Clone)]
pub struct InputPathValueParser;
impl clap::builder::TypedValueParser for InputPathValueParser {
    type Value = InputPath;

    fn parse_ref(
        &self,
        _cmd: &clap::Command,
        _arg: Option<&clap::Arg>,
        value: &std::ffi::OsStr,
    ) -> Result<Self::Value, clap::Error> {
        if value.eq_ignore_ascii_case("-") {
            Ok(InputPath::Stdin)
        } else {
            let os_string = value.to_owned();
            let path_buf = PathBuf::from(os_string);
            Ok(InputPath::File(path_buf))
        }
    }
}

// ALLOW: This `struct` is the user external API, and is expected to provide all
// the different supported format as different boolean options.
#[allow(clippy::struct_excessive_bools)]
#[derive(Debug, clap::Parser)]
#[command(version, author)]
pub struct Command {
    /// Enable parsing of the 'cargo audit' report
    #[cfg(feature = "audit")]
    #[arg(long)]
    pub audit: bool,
    /// Path of the 'cargo audit' report (or `-` for stdin)
    #[cfg(feature = "audit")]
    #[arg(long, default_value = "audit.json")]
    pub audit_path: InputPath,
    /// Enable parsing of the 'cargo clippy' report
    #[cfg(feature = "clippy")]
    #[arg(long)]
    pub clippy: bool,
    /// Path of the 'cargo clippy' report (or `-` for stdin)
    #[cfg(feature = "clippy")]
    #[arg(long, default_value = "clippy.json")]
    pub clippy_path: InputPath,
    /// Enable parsing of the 'cargo deny' report
    #[cfg(feature = "deny")]
    #[arg(long)]
    pub deny: bool,
    /// Path of the 'cargo deny' report (or `-` for stdin)
    #[cfg(feature = "deny")]
    #[arg(long, default_value = "deny.json")]
    pub deny_path: InputPath,
    /// Enable parsing of the 'cargo outdated' report
    #[cfg(feature = "outdated")]
    #[arg(long)]
    pub outdated: bool,
    /// Path of the 'cargo outdated' report (or `-` for stdin)
    #[cfg(feature = "outdated")]
    #[arg(long, default_value = "outdated.json")]
    pub outdated_path: InputPath,
    /// Enable parsing of the 'typos' report
    #[cfg(feature = "typos")]
    #[arg(long)]
    pub typos: bool,
    /// Path of the 'typos' report (or `-` for stdin)
    #[cfg(feature = "typos")]
    #[arg(long, default_value = "typos.json")]
    pub typos_path: InputPath,
    /// Enable parsing of the 'cargo udeps' report
    #[cfg(feature = "udeps")]
    #[arg(long)]
    pub udeps: bool,
    /// Path of the 'cargo udeps' report (or `-` for stdin)
    #[cfg(feature = "udeps")]
    #[arg(long, default_value = "udeps.json")]
    pub udeps_path: InputPath,
    /// Path to the generated sonar report
    #[arg(long, default_value = "sonar-issues.json")]
    pub sonar_path: PathBuf,
    /// Path to the generated codeclimate report
    #[arg(long, default_value = "codeclimate.json")]
    pub codeclimate_path: PathBuf,
    /// Generate completion rules for some shells
    #[arg(long, value_enum)]
    pub completion: Option<Shell>,
}

impl Command {
    #[inline]
    pub fn generate_completion(&self) {
        if let Some(ref shell) = self.completion {
            let mut app = Self::command();
            let mut fd = std::io::stdout();
            match *shell {
                Shell::Bash => generate(shells::Bash, &mut app, "cargo-sonar", &mut fd),
                Shell::Elvish => generate(shells::Elvish, &mut app, "cargo-sonar", &mut fd),
                Shell::Fish => generate(shells::Fish, &mut app, "cargo-sonar", &mut fd),
                Shell::Nushell => generate(
                    clap_complete_nushell::Nushell,
                    &mut app,
                    "cargo-sonar",
                    &mut fd,
                ),
                Shell::PowerShell => generate(shells::PowerShell, &mut app, "cargo-sonar", &mut fd),
                Shell::Zsh => generate(shells::Zsh, &mut app, "cargo-sonar", &mut fd),
            }
            std::process::exit(0);
        }
    }
}