agentstatedeveloper_cli/commands/
dead_code.rs1use anyhow::Result;
9use clap::Args;
10
11use agentstatedeveloper_core::{Engine, dead_code_report};
12
13use crate::config::Config;
14
15#[derive(Debug, Args)]
16pub struct DeadCodeArgs {
17 #[arg(long)]
19 pub agent: bool,
20
21 #[arg(long, default_value = "50")]
23 pub limit: usize,
24
25 #[arg(long)]
27 pub include_tests: bool,
28}
29
30pub fn run(cfg: &Config, args: DeadCodeArgs) -> Result<()> {
31 let engine = Engine::open_sqlite(&cfg.db_path)?;
32 let out = dead_code_report(&engine, args.limit, args.include_tests);
33
34 if args.agent {
35 println!("{}", serde_json::to_string_pretty(&out)?);
36 return Ok(());
37 }
38
39 let total = out["count"].as_u64().unwrap_or(0) as usize;
40 let candidates = out["candidates"].as_array().cloned().unwrap_or_default();
41 let excluded_handlers = out["excluded"]["route_handlers"].as_u64().unwrap_or(0);
42 let excluded_tests = out["excluded"]["test_symbols"].as_u64().unwrap_or(0);
43
44 println!(
45 "{} unreferenced function/method{} (no inbound call edges).",
46 total,
47 if total == 1 { "" } else { "s" }
48 );
49 if excluded_handlers > 0 || excluded_tests > 0 {
50 println!(
51 " (excluded {} route handler(s), {} test symbol(s))",
52 excluded_handlers, excluded_tests
53 );
54 }
55 for c in candidates.iter() {
56 println!(
57 " {}:{} {}",
58 c["file"].as_str().unwrap_or("?"),
59 c["line"].as_u64().unwrap_or(0),
60 c["qname"].as_str().unwrap_or("?")
61 );
62 }
63 if total > candidates.len() {
64 println!(" … {} more (use --limit)", total - candidates.len());
65 }
66 println!(
67 "\nNote: candidates only — static call graph misses public API, dynamic dispatch, and framework callbacks."
68 );
69 Ok(())
70}