cha_core/plugins/
hub_like.rs1use crate::{AnalysisContext, Finding, Location, Plugin, Severity, SmellCategory};
2
3pub struct HubLikeDependencyAnalyzer {
19 pub max_imports: usize,
20}
21
22impl Default for HubLikeDependencyAnalyzer {
23 fn default() -> Self {
24 Self { max_imports: 20 }
25 }
26}
27
28impl Plugin for HubLikeDependencyAnalyzer {
29 fn name(&self) -> &str {
30 "hub_like_dependency"
31 }
32
33 fn smells(&self) -> Vec<String> {
34 vec!["hub_like_dependency".into()]
35 }
36
37 fn description(&self) -> &str {
38 "Hub-like module with too many imports"
39 }
40
41 fn analyze(&self, ctx: &AnalysisContext) -> Vec<Finding> {
42 let count = ctx
43 .model
44 .imports
45 .iter()
46 .filter(|i| !i.is_module_decl)
47 .count();
48 if count <= self.max_imports {
49 return vec![];
50 }
51 let first = ctx.model.imports.first().map(|i| i.line).unwrap_or(1);
52 let first_col = ctx.model.imports.first().map(|i| i.col).unwrap_or(0);
53 let last = ctx.model.imports.last().map(|i| i.line).unwrap_or(1);
54 vec![Finding {
55 smell_name: "hub_like_dependency".into(),
56 category: SmellCategory::Couplers,
57 severity: Severity::Warning,
58 location: Location {
59 path: ctx.file.path.clone(),
60 start_line: first,
61 start_col: first_col,
62 end_line: last,
63 name: None,
64 ..Default::default()
65 },
66 message: format!(
67 "File has {} imports (threshold: {}), acting as a hub — consider splitting",
68 count, self.max_imports
69 ),
70 suggested_refactorings: vec!["Extract Module".into(), "Facade Pattern".into()],
71 actual_value: Some(count as f64),
72 threshold: Some(self.max_imports as f64),
73 }]
74 }
75}