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 description(&self) -> &str {
20        "Too many imports (high coupling)"
21    }
22
23    fn analyze(&self, ctx: &AnalysisContext) -> Vec<Finding> {
24        let mut findings = Vec::new();
25
26        // Skip Rust mod declarations — module organization, not coupling
27        // Skip module declarations — module organization, not coupling
28        let import_count = ctx
29            .model
30            .imports
31            .iter()
32            .filter(|i| !i.is_module_decl)
33            .count();
34
35        let first = ctx.model.imports.first().map(|i| i.line).unwrap_or(1);
36        let last = ctx.model.imports.last().map(|i| i.line).unwrap_or(1);
37
38        if import_count > self.max_imports {
39            findings.push(Finding {
40                smell_name: "high_coupling".into(),
41                category: SmellCategory::Couplers,
42                severity: if import_count > self.max_imports * 2 {
43                    Severity::Error
44                } else {
45                    Severity::Warning
46                },
47                location: Location {
48                    path: ctx.file.path.clone(),
49                    start_line: first,
50                    end_line: last,
51                    name: None,
52                },
53                message: format!(
54                    "File has {} imports (threshold: {})",
55                    import_count, self.max_imports
56                ),
57                suggested_refactorings: vec!["Move Method".into(), "Extract Class".into()],
58                actual_value: Some(import_count as f64),
59                threshold: Some(self.max_imports as f64),
60            });
61        }
62
63        findings
64    }
65}