Skip to main content

cha_core/plugins/
coupling.rs

1use crate::{AnalysisContext, Finding, Location, Plugin, Severity, SmellCategory};
2
3/// Detect high coupling via excessive imports.
4pub struct CouplingAnalyzer {
5    pub max_imports: usize,
6}
7
8impl Default for CouplingAnalyzer {
9    fn default() -> Self {
10        Self { max_imports: 15 }
11    }
12}
13
14impl Plugin for CouplingAnalyzer {
15    fn name(&self) -> &str {
16        "coupling"
17    }
18
19    fn smells(&self) -> Vec<String> {
20        vec!["high_coupling".into()]
21    }
22
23    fn description(&self) -> &str {
24        "Too many imports (high coupling)"
25    }
26
27    fn analyze(&self, ctx: &AnalysisContext) -> Vec<Finding> {
28        let mut findings = Vec::new();
29
30        // Skip Rust mod declarations — module organization, not coupling
31        // Skip module declarations — module organization, not coupling
32        let import_count = ctx
33            .model
34            .imports
35            .iter()
36            .filter(|i| !i.is_module_decl)
37            .count();
38
39        let first = ctx.model.imports.first().map(|i| i.line).unwrap_or(1);
40        let first_col = ctx.model.imports.first().map(|i| i.col).unwrap_or(0);
41        let last = ctx.model.imports.last().map(|i| i.line).unwrap_or(1);
42
43        if import_count > self.max_imports {
44            findings.push(Finding {
45                smell_name: "high_coupling".into(),
46                category: SmellCategory::Couplers,
47                severity: if import_count > self.max_imports * 2 {
48                    Severity::Error
49                } else {
50                    Severity::Warning
51                },
52                location: Location {
53                    path: ctx.file.path.clone(),
54                    start_line: first,
55                    start_col: first_col,
56                    end_line: last,
57                    name: None,
58                    ..Default::default()
59                },
60                message: format!(
61                    "File has {} imports (threshold: {})",
62                    import_count, self.max_imports
63                ),
64                suggested_refactorings: vec!["Move Method".into(), "Extract Class".into()],
65                actual_value: Some(import_count as f64),
66                threshold: Some(self.max_imports as f64),
67                risk_score: None,
68            });
69        }
70
71        findings
72    }
73}