Skip to main content

the_code_graph_cli/commands/
dead_code.rs

1use domain::error::Result;
2use domain::model::{DeadCodeConfig, SymbolKind};
3use domain::use_cases::dead_code::DeadCodeUseCase;
4
5use crate::commands::helpers::open_graph;
6use crate::commands::DeadCodeArgs;
7use crate::config::load_config;
8use crate::output::{print, OutputFormat};
9
10pub fn run_dead_code(args: &DeadCodeArgs, output_format: OutputFormat) -> Result<()> {
11    let (store, root) = open_graph()?;
12    let config = load_config(&root)?;
13
14    // Build DeadCodeConfig: defaults -> config.toml -> CLI flags
15    let mut dead_config = DeadCodeConfig::default();
16
17    // Apply config.toml [dead-code] section
18    if let Some(dc) = &config.dead_code {
19        if let Some(patterns) = &dc.exclude_patterns {
20            dead_config.exclude_patterns.extend(patterns.clone());
21        }
22        if let Some(patterns) = &dc.entry_point_patterns {
23            dead_config.entry_point_patterns.extend(patterns.clone());
24        }
25        if let Some(patterns) = &dc.migration_patterns {
26            dead_config.migration_patterns = patterns.clone();
27        }
28    }
29
30    // Apply CLI flags (unioned with config, not overriding)
31    dead_config
32        .exclude_patterns
33        .extend(args.exclude_pattern.clone());
34    dead_config.include_tests = args.include_tests;
35
36    // Parse --kind flags into SymbolKind filter
37    if !args.kind.is_empty() {
38        let kinds: Vec<SymbolKind> = args
39            .kind
40            .iter()
41            .filter_map(|k| match k.to_lowercase().as_str() {
42                "function" => Some(SymbolKind::Function),
43                "class" => Some(SymbolKind::Class),
44                "interface" => Some(SymbolKind::Interface),
45                "struct" => Some(SymbolKind::Struct),
46                "trait" => Some(SymbolKind::Trait),
47                "enum" => Some(SymbolKind::Enum),
48                "typealias" | "type_alias" => Some(SymbolKind::TypeAlias),
49                "method" => Some(SymbolKind::Method),
50                "property" => Some(SymbolKind::Property),
51                "const" => Some(SymbolKind::Const),
52                "macro" => Some(SymbolKind::Macro),
53                "variable" => Some(SymbolKind::Variable),
54                "component" => Some(SymbolKind::Component),
55                "test" => Some(SymbolKind::Test),
56                _ => None,
57            })
58            .collect();
59        if !kinds.is_empty() {
60            dead_config.kind_filter = Some(kinds);
61        }
62    }
63
64    let uc = DeadCodeUseCase::new(store);
65    let mut analysis = uc.analyze(&dead_config)?;
66
67    // Apply display-layer --limit
68    if let Some(limit) = args.limit {
69        analysis.dead_symbols.truncate(limit);
70    }
71
72    print(&analysis, output_format);
73    Ok(())
74}