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 description(&self) -> &str {
34 "Hub-like module with too many imports"
35 }
36
37 fn analyze(&self, ctx: &AnalysisContext) -> Vec<Finding> {
38 let count = ctx
39 .model
40 .imports
41 .iter()
42 .filter(|i| !i.is_module_decl)
43 .count();
44 if count <= self.max_imports {
45 return vec![];
46 }
47 let first = ctx.model.imports.first().map(|i| i.line).unwrap_or(1);
48 let first_col = ctx.model.imports.first().map(|i| i.col).unwrap_or(0);
49 let last = ctx.model.imports.last().map(|i| i.line).unwrap_or(1);
50 vec![Finding {
51 smell_name: "hub_like_dependency".into(),
52 category: SmellCategory::Couplers,
53 severity: Severity::Warning,
54 location: Location {
55 path: ctx.file.path.clone(),
56 start_line: first,
57 start_col: first_col,
58 end_line: last,
59 name: None,
60 ..Default::default()
61 },
62 message: format!(
63 "File has {} imports (threshold: {}), acting as a hub — consider splitting",
64 count, self.max_imports
65 ),
66 suggested_refactorings: vec!["Extract Module".into(), "Facade Pattern".into()],
67 actual_value: Some(count as f64),
68 threshold: Some(self.max_imports as f64),
69 }]
70 }
71}