use std::process::ExitCode;
use anyhow::Result;
use clap::{Parser, Subcommand};
use tldr_core::Language;
use tldr_cli::commands::remaining::{ApiCheckArgs, VulnArgs};
use tldr_cli::commands::{
ApiSurfaceArgs, AvailableArgs, BugbotCheckArgs, CacheClearArgs,
CacheStatsArgs, CallsArgs, ChangeImpactArgs, ChopArgs, ChurnArgs, ClonesArgs, CognitiveArgs,
ComplexityArgs, ContractsArgs, ContextArgs, CoverageArgs, DaemonNotifyArgs, DaemonQueryArgs,
DaemonStartArgs, DaemonStatusArgs, DaemonStopArgs, DeadArgs, DeadStoresArgs, DebtArgs,
DefinitionArgs, DepsArgs, DiagnosticsArgs, DiceArgs, DiffArgs, DoctorArgs, ExplainArgs,
ExtractArgs, FixArgs, HalsteadArgs, HealthArgs, HotspotsArgs, HubsArgs, ImpactArgs,
ImportersArgs, ImportsArgs, InheritanceArgs, InvariantsArgs, LocArgs, PatternsArgs,
ReachingDefsArgs, ReferencesArgs, SecureArgs, SliceArgs, SmartSearchArgs, SmellsArgs,
SpecsArgs, StatsArgs, StructureArgs, TaintArgs, TodoArgs, TreeArgs, VerifyArgs, WarmArgs,
WhatbreaksArgs,
};
use tldr_cli::commands::patterns::{
CohesionArgs, CouplingArgs, InterfaceArgs, ResourcesArgs, TemporalArgs,
};
#[cfg(feature = "semantic")]
use tldr_cli::commands::{EmbedArgs, SemanticArgs, SimilarArgs};
use tldr_cli::output::OutputFormat;
#[derive(Debug, Parser)]
#[command(
name = "tldr",
version,
about = "Token-efficient code analysis tool",
long_about = "TLDR provides code analysis commands optimized for LLM consumption.\n\n\
Commands are organized by analysis layer:\n\
- L1 (AST): tree, structure\n\
- L2 (Call Graph): calls, impact, dead\n\
- L3 (CFG): reaching-defs, available\n\
- L4 (DFG): dead-stores\n\
- L5 (PDG): slice\n\
- Search: search\n\
- Context: context\n\
- Quality: smells\n\
- Security: taint, vuln, secure"
)]
pub struct Cli {
#[command(subcommand)]
pub command: Command,
#[arg(long, short = 'f', global = true, default_value = "json")]
pub format: OutputFormat,
#[arg(long, short = 'l', global = true)]
pub lang: Option<Language>,
#[arg(long, short = 'q', global = true)]
pub quiet: bool,
#[arg(long, short = 'v', global = true)]
pub verbose: bool,
}
#[derive(Debug, Subcommand)]
pub enum Command {
#[command(visible_alias = "t")]
Tree(TreeArgs),
#[command(visible_alias = "s")]
Structure(StructureArgs),
#[command(visible_alias = "c")]
Calls(CallsArgs),
#[command(visible_alias = "i")]
Impact(ImpactArgs),
#[command(visible_alias = "d")]
Dead(DeadArgs),
#[command(name = "reaching-defs", visible_alias = "rd")]
ReachingDefs(ReachingDefsArgs),
#[command(visible_alias = "ta")]
Taint(TaintArgs),
#[command(visible_alias = "av")]
Available(AvailableArgs),
Slice(SliceArgs),
#[command(name = "search")]
SmartSearch(SmartSearchArgs),
Context(ContextArgs),
Smells(SmellsArgs),
#[command(visible_alias = "e")]
Extract(ExtractArgs),
Imports(ImportsArgs),
Importers(ImportersArgs),
Complexity(ComplexityArgs),
Churn(ChurnArgs),
Debt(DebtArgs),
#[command(visible_alias = "h")]
Health(HealthArgs),
Hubs(HubsArgs),
#[command(visible_alias = "wb")]
Whatbreaks(WhatbreaksArgs),
#[command(visible_alias = "p")]
Patterns(PatternsArgs),
#[command(visible_alias = "inh")]
Inheritance(InheritanceArgs),
#[command(visible_alias = "ci", name = "change-impact")]
ChangeImpact(ChangeImpactArgs),
#[command(visible_alias = "dep")]
Deps(DepsArgs),
#[command(visible_alias = "diag")]
Diagnostics(DiagnosticsArgs),
#[command(visible_alias = "doc")]
Doctor(DoctorArgs),
#[command(visible_alias = "refs")]
References(ReferencesArgs),
#[command(visible_alias = "cl")]
Clones(ClonesArgs),
Dice(DiceArgs),
Loc(LocArgs),
#[command(visible_alias = "cog")]
Cognitive(CognitiveArgs),
#[command(visible_alias = "hal")]
Halstead(HalsteadArgs),
#[command(visible_alias = "cov")]
Coverage(CoverageArgs),
#[command(visible_alias = "hot")]
Hotspots(HotspotsArgs),
#[cfg(feature = "semantic")]
#[command(visible_alias = "emb")]
Embed(EmbedArgs),
#[cfg(feature = "semantic")]
#[command(visible_alias = "sem")]
Semantic(SemanticArgs),
#[cfg(feature = "semantic")]
#[command(visible_alias = "sim")]
Similar(SimilarArgs),
#[command(subcommand)]
Daemon(DaemonCommand),
#[command(subcommand)]
Cache(CacheCommand),
#[command(visible_alias = "w")]
Warm(WarmArgs),
Stats(StatsArgs),
#[command(visible_alias = "surf")]
Surface(ApiSurfaceArgs),
#[command(visible_alias = "con")]
Contracts(ContractsArgs),
#[command(visible_alias = "ds")]
DeadStores(DeadStoresArgs),
#[command(visible_alias = "chp")]
Chop(ChopArgs),
#[command(visible_alias = "sp")]
Specs(SpecsArgs),
#[command(visible_alias = "inv")]
Invariants(InvariantsArgs),
#[command(visible_alias = "ver")]
Verify(VerifyArgs),
#[command(visible_alias = "coh")]
Cohesion(CohesionArgs),
#[command(visible_alias = "tem")]
Temporal(TemporalArgs),
#[command(visible_alias = "res")]
Resources(ResourcesArgs),
#[command(visible_alias = "coup")]
Coupling(CouplingArgs),
#[command(visible_alias = "iface")]
Interface(InterfaceArgs),
#[command(visible_alias = "exp")]
Explain(ExplainArgs),
Todo(TodoArgs),
#[command(visible_alias = "sec")]
Secure(SecureArgs),
#[command(visible_alias = "def")]
Definition(DefinitionArgs),
#[command(visible_alias = "df")]
Diff(DiffArgs),
#[command(name = "api-check", visible_alias = "ac")]
ApiCheck(ApiCheckArgs),
Vuln(VulnArgs),
#[command(visible_alias = "fx")]
Fix(FixArgs),
#[command(subcommand)]
Bugbot(BugbotCommand),
}
#[derive(Debug, Subcommand)]
pub enum DaemonCommand {
Start(DaemonStartArgs),
Stop(DaemonStopArgs),
Status(DaemonStatusArgs),
Query(DaemonQueryArgs),
Notify(DaemonNotifyArgs),
}
#[derive(Debug, Subcommand)]
pub enum CacheCommand {
Stats(CacheStatsArgs),
Clear(CacheClearArgs),
}
#[derive(Debug, Subcommand)]
pub enum BugbotCommand {
Check(BugbotCheckArgs),
}
fn main() -> ExitCode {
let cli = Cli::parse();
if cli.verbose {
std::env::set_var("TLDR_LOG", "debug");
}
let result = run_command(&cli);
match result {
Ok(()) => ExitCode::SUCCESS,
Err(e) => {
eprintln!("Error: {}", e);
if cli.verbose {
let mut source = e.source();
while let Some(err) = source {
eprintln!(" Caused by: {}", err);
source = err.source();
}
}
if let Some(bugbot_err) =
e.downcast_ref::<tldr_cli::commands::bugbot::BugbotExitError>()
{
ExitCode::from(bugbot_err.exit_code())
} else if let Some(tldr_err) = e.downcast_ref::<tldr_core::TldrError>() {
ExitCode::from(tldr_err.exit_code() as u8)
} else if let Some(remaining_err) =
e.downcast_ref::<tldr_cli::commands::remaining::RemainingError>()
{
ExitCode::from(remaining_err.exit_code() as u8)
} else {
ExitCode::FAILURE
}
}
}
}
fn run_command(cli: &Cli) -> Result<()> {
match &cli.command {
Command::Tree(args) => args.run(cli.format, cli.quiet),
Command::Structure(args) => args.run(cli.format, cli.quiet),
Command::Calls(args) => args.run(cli.format, cli.quiet),
Command::Impact(args) => args.run(cli.format, cli.quiet),
Command::Dead(args) => args.run(cli.format, cli.quiet),
Command::ReachingDefs(args) => args.run(cli.format, cli.quiet),
Command::Taint(args) => args.run(cli.format, cli.quiet),
Command::Available(args) => args.run(cli.format, cli.quiet),
Command::Slice(args) => args.run(cli.format, cli.quiet),
Command::SmartSearch(args) => args.run(cli.format, cli.quiet),
Command::Context(args) => args.run(cli.format, cli.quiet),
Command::Smells(args) => args.run(cli.format, cli.quiet),
Command::Extract(args) => args.run(cli.format, cli.quiet),
Command::Imports(args) => args.run(cli.format, cli.quiet),
Command::Importers(args) => args.run(cli.format, cli.quiet),
Command::Complexity(args) => args.run(cli.format, cli.quiet),
Command::Churn(args) => args.run(cli.format, cli.quiet),
Command::Debt(args) => args.run(cli.format, cli.quiet, cli.lang),
Command::Health(args) => args.run(cli.format, cli.quiet, cli.lang),
Command::Hubs(args) => args.run(cli.format, cli.quiet),
Command::Whatbreaks(args) => args.run(cli.format, cli.quiet),
Command::Patterns(args) => args.run(cli.format, cli.quiet),
Command::Inheritance(args) => args.run(cli.format, cli.quiet),
Command::ChangeImpact(args) => args.run(cli.format, cli.quiet),
Command::Deps(args) => args.run(cli.format, cli.quiet),
Command::Diagnostics(args) => args.run(cli.format, cli.quiet),
Command::Doctor(args) => args.run(cli.format, cli.quiet),
Command::References(args) => args.run(cli.format, cli.quiet),
Command::Clones(args) => args.run(cli.format, cli.quiet),
Command::Dice(args) => args.run(cli.format, cli.quiet),
Command::Loc(args) => args.run(cli.format, cli.quiet),
Command::Cognitive(args) => args.run(cli.format, cli.quiet),
Command::Halstead(args) => args.run(cli.format, cli.quiet),
Command::Coverage(args) => args.run(cli.format, cli.quiet),
Command::Hotspots(args) => args.run(cli.format, cli.quiet),
#[cfg(feature = "semantic")]
Command::Embed(args) => args.run(cli.format, cli.quiet),
#[cfg(feature = "semantic")]
Command::Semantic(args) => args.run(cli.format, cli.quiet),
#[cfg(feature = "semantic")]
Command::Similar(args) => args.run(cli.format, cli.quiet),
Command::Daemon(daemon_cmd) => match daemon_cmd {
DaemonCommand::Start(args) => args.run(cli.format, cli.quiet),
DaemonCommand::Stop(args) => args.run(cli.format, cli.quiet),
DaemonCommand::Status(args) => args.run(cli.format, cli.quiet),
DaemonCommand::Query(args) => args.run(cli.format, cli.quiet),
DaemonCommand::Notify(args) => args.run(cli.format, cli.quiet),
},
Command::Cache(cache_cmd) => match cache_cmd {
CacheCommand::Stats(args) => args.run(cli.format, cli.quiet),
CacheCommand::Clear(args) => args.run(cli.format, cli.quiet),
},
Command::Warm(args) => args.run(cli.format, cli.quiet),
Command::Stats(args) => args.run(cli.format, cli.quiet),
Command::Surface(args) => args.run(cli.format, cli.quiet, cli.lang),
Command::Contracts(args) => args.run(cli.format, cli.quiet),
Command::DeadStores(args) => args.run(cli.format, cli.quiet),
Command::Chop(args) => args.run(cli.format, cli.quiet),
Command::Specs(args) => args.run(cli.format, cli.quiet),
Command::Invariants(args) => args.run(cli.format, cli.quiet),
Command::Verify(args) => args.run(cli.format, cli.quiet),
Command::Cohesion(args) => args.run(cli.format),
Command::Temporal(args) => args.run(cli.format),
Command::Resources(args) => args.run(cli.format),
Command::Coupling(args) => {
tldr_cli::commands::patterns::coupling::run(args.clone(), cli.format)
}
Command::Interface(args) => {
tldr_cli::commands::patterns::interface::run(args.clone(), cli.format)
}
Command::Explain(args) => args.run(cli.format, cli.quiet),
Command::Todo(args) => args.run(cli.format, cli.quiet, cli.lang),
Command::Secure(args) => args.run(cli.format),
Command::Definition(args) => args.run(cli.format, cli.quiet, cli.lang),
Command::Diff(args) => args.run(cli.format),
Command::ApiCheck(args) => args.run(cli.format, cli.quiet),
Command::Vuln(args) => args.run(cli.format),
Command::Fix(args) => args.run(cli.format, cli.quiet, cli.lang),
Command::Bugbot(bugbot_cmd) => match bugbot_cmd {
BugbotCommand::Check(args) => args.run(cli.format, cli.quiet, cli.lang),
},
}
}