the_code_graph_domain/use_cases/
dead_code.rs1use crate::analysis::dead_code::detect_dead_code;
2use crate::error::Result;
3use crate::model::*;
4use crate::ports::GraphStore;
5
6pub struct DeadCodeUseCase<S> {
7 store: S,
8}
9
10impl<S: GraphStore> DeadCodeUseCase<S> {
11 pub fn new(store: S) -> Self {
12 Self { store }
13 }
14
15 pub fn analyze(&self, config: &DeadCodeConfig) -> Result<DeadCodeAnalysis> {
17 let symbols = self.store.all_symbols()?;
18 let edges = self.store.all_edges()?;
19 Ok(detect_dead_code(&symbols, &edges, config))
20 }
21}
22
23#[cfg(test)]
24mod tests {
25 use super::*;
26 use crate::model::{Edge, EdgeKind, Location, SymbolKind, SymbolNode, Visibility};
27 use crate::test_support::InMemoryGraphStore;
28
29 fn build_store() -> InMemoryGraphStore {
30 let mut store = InMemoryGraphStore::new();
31
32 store.insert_symbol(SymbolNode {
34 name: "orphan_fn".into(),
35 qualified_name: "src/old.rs::orphan_fn".into(),
36 kind: SymbolKind::Function,
37 location: Location {
38 file: "src/old.rs".into(),
39 line_start: 10,
40 line_end: 20,
41 col_start: 0,
42 col_end: 0,
43 },
44 visibility: Visibility::Private,
45 is_exported: false,
46 is_async: false,
47 is_test: false,
48 decorators: vec![],
49 signature: None,
50 });
51
52 store.insert_symbol(SymbolNode {
54 name: "active_fn".into(),
55 qualified_name: "src/core.rs::active_fn".into(),
56 kind: SymbolKind::Function,
57 location: Location {
58 file: "src/core.rs".into(),
59 line_start: 1,
60 line_end: 10,
61 col_start: 0,
62 col_end: 0,
63 },
64 visibility: Visibility::Public,
65 is_exported: false,
66 is_async: false,
67 is_test: false,
68 decorators: vec![],
69 signature: None,
70 });
71
72 store.insert_edge(Edge {
74 kind: EdgeKind::Calls,
75 source: "src/main.rs::main".into(),
76 target: "src/core.rs::active_fn".into(),
77 metadata: None,
78 });
79
80 store
81 }
82
83 #[test]
84 fn use_case_detects_dead_code() {
85 let store = build_store();
86 let uc = DeadCodeUseCase::new(store);
87 let result = uc.analyze(&DeadCodeConfig::default()).unwrap();
88
89 assert_eq!(result.summary.total_symbols, 2);
90 assert_eq!(result.dead_symbols.len(), 1);
91 assert_eq!(
92 result.dead_symbols[0].qualified_name,
93 "src/old.rs::orphan_fn"
94 );
95 }
96
97 #[test]
98 fn use_case_with_include_tests() {
99 let mut store = build_store();
100 store.insert_symbol(SymbolNode {
101 name: "old_test".into(),
102 qualified_name: "tests/old.rs::old_test".into(),
103 kind: SymbolKind::Test,
104 location: Location {
105 file: "tests/old.rs".into(),
106 line_start: 1,
107 line_end: 5,
108 col_start: 0,
109 col_end: 0,
110 },
111 visibility: Visibility::Private,
112 is_exported: false,
113 is_async: false,
114 is_test: true,
115 decorators: vec![],
116 signature: None,
117 });
118
119 let uc = DeadCodeUseCase::new(store.clone());
121 let result = uc.analyze(&DeadCodeConfig::default()).unwrap();
122 assert_eq!(result.dead_symbols.len(), 1); let config = DeadCodeConfig {
126 include_tests: true,
127 ..DeadCodeConfig::default()
128 };
129 let result = uc.analyze(&config).unwrap();
130 assert_eq!(result.dead_symbols.len(), 2);
131 }
132}