use std::path::PathBuf;
use clap::{Args, ValueEnum};
use serde::{Deserialize, Serialize};
use super::AnalysisFormat;
#[derive(Args, Debug, Clone)]
#[command(
after_help = "Examples:\n tokmd analyze --preset receipt --format md\n tokmd analyze . --preset risk --output-dir .runs/analysis"
)]
pub struct CliAnalyzeArgs {
#[arg(value_name = "INPUT", default_value = ".")]
pub inputs: Vec<PathBuf>,
#[arg(long, value_enum)]
pub preset: Option<AnalysisPreset>,
#[arg(long, value_enum)]
pub format: Option<AnalysisFormat>,
#[arg(long)]
pub window: Option<usize>,
#[arg(long, action = clap::ArgAction::SetTrue, conflicts_with = "no_git")]
pub git: bool,
#[arg(long = "no-git", action = clap::ArgAction::SetTrue, conflicts_with = "git")]
pub no_git: bool,
#[arg(long)]
pub output_dir: Option<PathBuf>,
#[arg(long)]
pub max_files: Option<usize>,
#[arg(long)]
pub max_bytes: Option<u64>,
#[arg(long)]
pub max_file_bytes: Option<u64>,
#[arg(long)]
pub max_commits: Option<usize>,
#[arg(long)]
pub max_commit_files: Option<usize>,
#[arg(long, value_enum)]
pub granularity: Option<ImportGranularity>,
#[arg(long)]
pub effort_model: Option<EffortModelKind>,
#[arg(long)]
pub effort_layer: Option<EffortLayer>,
#[arg(long = "effort-base-ref")]
pub effort_base_ref: Option<String>,
#[arg(long = "effort-head-ref")]
pub effort_head_ref: Option<String>,
#[arg(long)]
pub monte_carlo: bool,
#[arg(long = "mc-iterations")]
pub mc_iterations: Option<usize>,
#[arg(long = "mc-seed")]
pub mc_seed: Option<u64>,
#[arg(long)]
pub detail_functions: bool,
#[arg(long)]
pub near_dup: bool,
#[arg(long, default_value = "0.80")]
pub near_dup_threshold: f64,
#[arg(long, default_value = "2000")]
pub near_dup_max_files: usize,
#[arg(long, value_enum)]
pub near_dup_scope: Option<NearDupScope>,
#[arg(long, default_value = "10000")]
pub near_dup_max_pairs: usize,
#[arg(long, value_name = "GLOB")]
pub near_dup_exclude: Vec<String>,
#[arg(long, value_name = "KEY")]
pub explain: Option<String>,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum AnalysisPreset {
Receipt,
Estimate,
BunUb,
Health,
Risk,
Supply,
Architecture,
Topics,
Security,
Identity,
Git,
Deep,
Fun,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum ImportGranularity {
Module,
File,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum EffortModelKind {
Cocomo81Basic,
Cocomo2Early,
Ensemble,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum EffortLayer {
Headline,
Why,
Full,
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub enum NearDupScope {
#[default]
Module,
Lang,
Global,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn analysis_preset_serde_roundtrip() {
for variant in [
AnalysisPreset::Receipt,
AnalysisPreset::Estimate,
AnalysisPreset::BunUb,
AnalysisPreset::Health,
AnalysisPreset::Risk,
AnalysisPreset::Supply,
AnalysisPreset::Architecture,
AnalysisPreset::Topics,
AnalysisPreset::Security,
AnalysisPreset::Identity,
AnalysisPreset::Git,
AnalysisPreset::Deep,
AnalysisPreset::Fun,
] {
let json = serde_json::to_string(&variant).unwrap();
let back: AnalysisPreset = serde_json::from_str(&json).unwrap();
assert_eq!(back, variant);
}
}
#[test]
fn analysis_preset_uses_kebab_case() {
assert_eq!(
serde_json::to_string(&AnalysisPreset::Receipt).unwrap(),
"\"receipt\""
);
assert_eq!(
serde_json::to_string(&AnalysisPreset::Deep).unwrap(),
"\"deep\""
);
assert_eq!(
serde_json::to_string(&AnalysisPreset::BunUb).unwrap(),
"\"bun-ub\""
);
}
#[test]
fn near_dup_scope_default_is_module() {
assert_eq!(NearDupScope::default(), NearDupScope::Module);
}
}