use anyhow::Result;
use clap::Args;
use agentstatedeveloper_core::{Engine, dead_code_report};
use crate::config::Config;
#[derive(Debug, Args)]
pub struct DeadCodeArgs {
#[arg(long)]
pub agent: bool,
#[arg(long, default_value = "50")]
pub limit: usize,
#[arg(long)]
pub include_tests: bool,
}
pub fn run(cfg: &Config, args: DeadCodeArgs) -> Result<()> {
let engine = Engine::open_sqlite(&cfg.db_path)?;
let out = dead_code_report(&engine, args.limit, args.include_tests);
if args.agent {
println!("{}", serde_json::to_string_pretty(&out)?);
return Ok(());
}
let total = out["count"].as_u64().unwrap_or(0) as usize;
let candidates = out["candidates"].as_array().cloned().unwrap_or_default();
let excluded_handlers = out["excluded"]["route_handlers"].as_u64().unwrap_or(0);
let excluded_tests = out["excluded"]["test_symbols"].as_u64().unwrap_or(0);
println!(
"{} unreferenced function/method{} (no inbound call edges).",
total,
if total == 1 { "" } else { "s" }
);
if excluded_handlers > 0 || excluded_tests > 0 {
println!(
" (excluded {} route handler(s), {} test symbol(s))",
excluded_handlers, excluded_tests
);
}
for c in candidates.iter() {
println!(
" {}:{} {}",
c["file"].as_str().unwrap_or("?"),
c["line"].as_u64().unwrap_or(0),
c["qname"].as_str().unwrap_or("?")
);
}
if total > candidates.len() {
println!(" … {} more (use --limit)", total - candidates.len());
}
println!(
"\nNote: candidates only — static call graph misses public API, dynamic dispatch, and framework callbacks."
);
Ok(())
}