kingfisher-bin 2.8.0

MongoDB's blazingly fast and accurate secret scanning and validation tool
use clap::{Args, Subcommand, ValueEnum, ValueHint};
use strum::Display;
use url::Url;

use crate::cli::commands::output::OutputArgs;

/// Top-level GitHub command group
#[derive(Args, Debug)]
pub struct GitHubArgs {
    #[command(subcommand)]
    pub command: GitHubCommand,

    /// Override GitHub API URL (e.g. Enterprise)
    #[arg(global = true, long, default_value = "https://api.github.com/", value_hint = ValueHint::Url)]
    pub github_api_url: Url,
}

#[derive(Subcommand, Debug)]
pub enum GitHubCommand {
    /// Interact with GitHub repositories
    #[command(subcommand)]
    Repos(GitHubReposCommand),
}

#[derive(Subcommand, Debug)]
pub enum GitHubReposCommand {
    /// List repositories for a user or organization
    List(GitHubReposListArgs),
}

/// `kingfisher github repos`
#[derive(Args, Debug, Clone)]
pub struct GitHubReposListArgs {
    #[command(flatten)]
    pub repo_specifiers: GitHubRepoSpecifiers,

    #[command(flatten)]
    pub output_args: OutputArgs<GitHubOutputFormat>,
}

/// Options for selecting GitHub repos
#[derive(Args, Debug, Clone)]
pub struct GitHubRepoSpecifiers {
    /// Repositories belonging to these users
    #[arg(long, alias = "github-user")]
    pub user: Vec<String>,

    /// Include public gists belonging to the specified users
    #[arg(long, requires = "user")]
    pub include_gists: bool,

    /// Repositories belonging to these organizations
    #[arg(long, alias = "org", alias = "github-organization", alias = "github-org")]
    pub organization: Vec<String>,

    /// Skip specific repositories when enumerating GitHub sources (format: owner/repo)
    #[arg(long = "github-exclude", alias = "github-exclude-repo", value_name = "OWNER/REPO")]
    pub exclude_repos: Vec<String>,

    /// Repositories for all organizations (Enterprise only)
    #[arg(
        long,
        alias = "all-orgs",
        alias = "all-github-organizations",
        alias = "all-github-orgs",
        requires = "api_url"
    )]
    pub all_organizations: bool,

    /// Filter by repository type
    #[arg(long, default_value_t = GitHubRepoType::Source, alias = "github-repo-type")]
    pub repo_type: GitHubRepoType,
}

impl GitHubRepoSpecifiers {
    /// Check if no GitHub sources are specified
    pub fn is_empty(&self) -> bool {
        self.user.is_empty() && self.organization.is_empty() && !self.all_organizations
    }
}

/// GitHub repository type filter
#[derive(Copy, Clone, Debug, Display, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
#[strum(serialize_all = "kebab-case")]
pub enum GitHubRepoType {
    /// Only source repositories (not forks)
    Source,
    /// Only fork repositories
    #[value(alias = "forks")]
    Fork,
    /// All repositories (source and forks)
    All,
}

impl From<GitHubRepoType> for crate::github::RepoType {
    fn from(val: GitHubRepoType) -> Self {
        match val {
            GitHubRepoType::Source => crate::github::RepoType::Source,
            GitHubRepoType::Fork => crate::github::RepoType::Fork,
            GitHubRepoType::All => crate::github::RepoType::All,
        }
    }
}

/// Output formats for GitHub commands
#[derive(Copy, Clone, Debug, ValueEnum, Display)]
#[strum(serialize_all = "kebab-case")]
pub enum GitHubOutputFormat {
    Pretty,
    Json,
    Jsonl,
    Bson,
    Sarif,
}

// -----------------------------------------------------------------------------
// Git Repository Cloning/History
// -----------------------------------------------------------------------------
#[derive(Copy, Clone, Debug, Display, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
#[strum(serialize_all = "kebab-case")]
pub enum GitCloneMode {
    /// Equivalent to `git clone --bare`
    Bare,
    /// Equivalent to `git clone --mirror`, often clones extra objects
    Mirror,
}

/// Specifies how to handle a repository's Git history.
#[derive(Copy, Clone, Debug, Display, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
#[strum(serialize_all = "kebab-case")]
pub enum GitHistoryMode {
    /// Scan all history
    Full,
    /// Ignore history entirely
    None,
}