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 last = ctx.model.imports.last().map(|i| i.line).unwrap_or(1);
49 vec![Finding {
50 smell_name: "hub_like_dependency".into(),
51 category: SmellCategory::Couplers,
52 severity: Severity::Warning,
53 location: Location {
54 path: ctx.file.path.clone(),
55 start_line: first,
56 end_line: last,
57 name: None,
58 },
59 message: format!(
60 "File has {} imports (threshold: {}), acting as a hub — consider splitting",
61 count, self.max_imports
62 ),
63 suggested_refactorings: vec!["Extract Module".into(), "Facade Pattern".into()],
64 actual_value: Some(count as f64),
65 threshold: Some(self.max_imports as f64),
66 }]
67 }
68}