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 first_col = ctx.model.imports.first().map(|i| i.col).unwrap_or(0);
37        let last = ctx.model.imports.last().map(|i| i.line).unwrap_or(1);
38
39        if import_count > self.max_imports {
40            findings.push(Finding {
41                smell_name: "high_coupling".into(),
42                category: SmellCategory::Couplers,
43                severity: if import_count > self.max_imports * 2 {
44                    Severity::Error
45                } else {
46                    Severity::Warning
47                },
48                location: Location {
49                    path: ctx.file.path.clone(),
50                    start_line: first,
51                    start_col: first_col,
52                    end_line: last,
53                    name: None,
54                    ..Default::default()
55                },
56                message: format!(
57                    "File has {} imports (threshold: {})",
58                    import_count, self.max_imports
59                ),
60                suggested_refactorings: vec!["Move Method".into(), "Extract Class".into()],
61                actual_value: Some(import_count as f64),
62                threshold: Some(self.max_imports as f64),
63            });
64        }
65
66        findings
67    }
68}