Skip to main content

cha_core/plugins/
middle_man.rs

1use crate::{AnalysisContext, Finding, Location, Plugin, Severity, SmellCategory};
2
3/// Detect classes where most methods only delegate to another object.
4pub struct MiddleManAnalyzer {
5    pub min_methods: usize,
6    pub delegation_ratio: f64,
7}
8
9impl Default for MiddleManAnalyzer {
10    fn default() -> Self {
11        Self {
12            min_methods: 3,
13            delegation_ratio: 0.5,
14        }
15    }
16}
17
18impl Plugin for MiddleManAnalyzer {
19    fn name(&self) -> &str {
20        "middle_man"
21    }
22
23    fn smells(&self) -> Vec<String> {
24        vec!["middle_man".into()]
25    }
26
27    fn description(&self) -> &str {
28        "Class that only delegates to others"
29    }
30
31    fn analyze(&self, ctx: &AnalysisContext) -> Vec<Finding> {
32        ctx.model
33            .classes
34            .iter()
35            .filter(|c| {
36                c.method_count >= self.min_methods
37                    && c.delegating_method_count > 0
38                    && (c.delegating_method_count as f64 / c.method_count as f64)
39                        >= self.delegation_ratio
40            })
41            .map(|c| Finding {
42                smell_name: "middle_man".into(),
43                category: SmellCategory::Couplers,
44                severity: Severity::Hint,
45                location: Location {
46                    path: ctx.file.path.clone(),
47                    start_line: c.start_line,
48                    start_col: c.name_col,
49                    end_line: c.start_line,
50                    end_col: c.name_end_col,
51                    name: Some(c.name.clone()),
52                },
53                message: format!(
54                    "Class `{}` delegates {}/{} methods, acting as a middle man",
55                    c.name, c.delegating_method_count, c.method_count
56                ),
57                suggested_refactorings: vec!["Remove Middle Man".into()],
58                actual_value: Some(c.delegating_method_count as f64 / c.method_count as f64),
59                threshold: Some(self.delegation_ratio),
60            })
61            .collect()
62    }
63}