use std::{path::PathBuf, time::Duration};
use clap::{ArgAction, Args, Subcommand, ValueEnum, ValueHint};
use strum::Display;
use crate::{
cli::commands::{output::OutputArgs, scan::ConfidenceLevel},
rules_database::{DEFAULT_RULE_CACHE_MAX_AGE, DEFAULT_RULE_CACHE_MAX_ENTRIES},
};
#[derive(Args, Debug, Clone, Default)]
pub struct RuleSpecifierArgs {
#[arg(global = true, long, alias="rules", value_hint=ValueHint::AnyPath)]
pub rules_path: Vec<PathBuf>,
#[arg(global = true, long, default_values_t=["all".to_string()])]
pub rule: Vec<String>,
#[arg(global = true, long = "exclude-rule", value_name = "RULE")]
pub exclude_rule: Vec<String>,
#[arg(global = true, long, default_value_t=true, action=ArgAction::Set)]
pub load_builtins: bool,
}
#[derive(Args, Debug, Clone)]
pub struct RuleCacheArgs {
#[arg(
global = true,
long = "rule-cache",
default_value_t = false,
conflicts_with = "no_rule_cache",
hide = true
)]
pub rule_cache: bool,
#[arg(
global = true,
long = "no-rule-cache",
default_value_t = false,
conflicts_with = "rule_cache"
)]
pub no_rule_cache: bool,
#[arg(
global = true,
long = "rule-cache-dir",
env = "KF_RULE_CACHE_DIR",
value_name = "PATH",
value_hint = ValueHint::DirPath
)]
pub rule_cache_dir: Option<PathBuf>,
#[arg(global = true, long = "prune-rule-cache", default_value_t = false)]
pub prune_rule_cache: bool,
#[arg(
global = true,
long = "rule-cache-max-entries",
default_value_t = DEFAULT_RULE_CACHE_MAX_ENTRIES,
value_name = "N"
)]
pub rule_cache_max_entries: usize,
#[arg(
global = true,
long = "rule-cache-max-age",
default_value = "30d",
value_name = "DURATION",
value_parser = parse_duration_arg
)]
pub rule_cache_max_age: Duration,
}
impl RuleCacheArgs {
pub fn enabled(&self) -> bool {
self.rule_cache || !self.no_rule_cache
}
}
impl Default for RuleCacheArgs {
fn default() -> Self {
Self {
rule_cache: false,
no_rule_cache: false,
rule_cache_dir: None,
prune_rule_cache: false,
rule_cache_max_entries: DEFAULT_RULE_CACHE_MAX_ENTRIES,
rule_cache_max_age: DEFAULT_RULE_CACHE_MAX_AGE,
}
}
}
#[derive(Args, Debug, Clone, Default)]
pub struct RuleCacheDirArgs {
#[arg(
long = "rule-cache-dir",
env = "KF_RULE_CACHE_DIR",
value_name = "PATH",
value_hint = ValueHint::DirPath
)]
pub rule_cache_dir: Option<PathBuf>,
}
#[derive(Args, Debug, Clone)]
pub struct RuleCachePruneArgs {
#[command(flatten)]
pub cache: RuleCacheDirArgs,
#[arg(long = "rule-cache-max-entries", default_value_t = DEFAULT_RULE_CACHE_MAX_ENTRIES, value_name = "N")]
pub max_entries: usize,
#[arg(
long = "rule-cache-max-age",
default_value = "30d",
value_name = "DURATION",
value_parser = parse_duration_arg
)]
pub max_age: Duration,
#[arg(long, default_value_t = false)]
pub dry_run: bool,
}
#[derive(Args, Debug)]
pub struct RulesArgs {
#[command(subcommand)]
pub command: RulesCommand,
}
#[derive(Subcommand, Debug)]
pub enum RulesCommand {
Check(RulesCheckArgs),
#[command(name = "compile-cache")]
CompileCache(RulesCompileCacheArgs),
#[command(name = "prune-cache")]
PruneCache(RuleCachePruneArgs),
List(RulesListArgs),
}
#[derive(Args, Debug)]
pub struct RulesCheckArgs {
#[arg(long, short = 'W')]
pub warnings_as_errors: bool,
#[command(flatten)]
pub rules: RuleSpecifierArgs,
}
#[derive(Args, Debug)]
pub struct RulesListArgs {
#[command(flatten)]
pub rules: RuleSpecifierArgs,
#[command(flatten)]
pub output_args: OutputArgs<RulesListOutputFormat>,
#[cfg(debug_assertions)]
#[arg(long)]
pub show_validation: bool,
}
#[derive(Args, Debug)]
pub struct RulesCompileCacheArgs {
#[command(flatten)]
pub rules: RuleSpecifierArgs,
#[arg(global = true, long, short = 'c', default_value = "medium")]
pub confidence: ConfidenceLevel,
#[command(flatten)]
pub cache: RuleCacheDirArgs,
}
#[derive(Copy, Clone, Debug, Display, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
#[strum(serialize_all = "kebab-case")]
pub enum RulesListOutputFormat {
Pretty,
Json,
}
fn parse_duration_arg(value: &str) -> Result<Duration, String> {
humantime::parse_duration(value).map_err(|err| err.to_string())
}