use anyhow::{anyhow, Result};
use clap::{Args, Parser, Subcommand, ValueHint};
use std::path::PathBuf;
#[derive(Parser)]
#[command(name = "gmap")]
#[command(about = "Git repository analysis tool for churn, heatmap, and exports")]
#[command(version)]
pub struct Cli {
#[clap(flatten)]
pub common: CommonArgs,
#[command(subcommand)]
pub command: Commands,
}
#[derive(Args, Clone)]
pub struct CommonArgs {
#[arg(long, help = "Path to git repository", value_hint = ValueHint::DirPath)]
pub repo: Option<PathBuf>,
#[arg(long, help = "Path to cache database", value_hint = ValueHint::DirPath)]
pub cache: Option<PathBuf>,
#[arg(long, help = "Include merge commits", default_value_t = true)]
pub include_merges: bool,
#[arg(long, help = "Include binary files", default_value_t = false)]
pub binary: bool,
#[arg(long, help = "Start from this commit or date (RFC3339, YYYY-MM-DD, or natural language)")]
pub since: Option<String>,
#[arg(long, help = "End at this commit or date (RFC3339, YYYY-MM-DD, or natural language)")]
pub until: Option<String>,
}
#[derive(Subcommand)]
pub enum Commands {
Churn {
#[arg(long, help = "Output as JSON", conflicts_with = "ndjson")]
json: bool,
#[arg(long, help = "Output as NDJSON", conflicts_with = "json")]
ndjson: bool,
#[arg(long, help = "Directory depth for aggregation")]
depth: Option<u32>,
#[arg(help = "Path prefix to analyze")]
path: Option<String>,
},
Heat {
#[arg(long, help = "Output as JSON", conflicts_with = "ndjson")]
json: bool,
#[arg(long, help = "Output as NDJSON", conflicts_with = "json")]
ndjson: bool,
#[arg(long = "interactive", alias = "tui", alias = "ui", help = "Enable interactive terminal UI")]
interactive: bool,
#[arg(help = "Path prefix to analyze")]
path: Option<String>,
},
Export {
#[arg(long, help = "Output as JSON", conflicts_with = "ndjson")]
json: bool,
#[arg(long, help = "Output as NDJSON", conflicts_with = "json")]
ndjson: bool,
},
}
impl Cli {
pub fn execute(self) -> Result<()> {
match self.command {
Commands::Churn { json, ndjson, depth, path } => {
crate::churn::exec(self.common, depth, json, ndjson, path)
}
Commands::Heat { json, ndjson, interactive, path } => {
if interactive {
crate::tui::run(&self.common, path).map_err(|e| anyhow!(e))
} else {
crate::heat::exec(self.common, json, ndjson, path)
}
}
Commands::Export { json, ndjson } => {
crate::export::exec(self.common, json, ndjson)
}
}
}
}