Skip to main content

boundary_core/
metrics_report.rs

1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4
5use crate::types::ArchLayer;
6
7/// Classification coverage: how much of the codebase is classified into layers.
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct ClassificationCoverage {
10    pub total_components: usize,
11    pub classified: usize,
12    pub cross_cutting: usize,
13    pub unclassified: usize,
14    pub coverage_percentage: f64,
15    pub unclassified_paths: Vec<String>,
16}
17
18/// Detailed metrics beyond scores.
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct MetricsReport {
21    pub components_by_kind: HashMap<String, usize>,
22    pub components_by_layer: HashMap<String, usize>,
23    pub violations_by_kind: HashMap<String, usize>,
24    pub dependency_depth: DependencyDepthMetrics,
25    pub layer_coupling: LayerCouplingMatrix,
26    #[serde(default, skip_serializing_if = "Option::is_none")]
27    pub classification_coverage: Option<ClassificationCoverage>,
28}
29
30/// Dependency depth metrics.
31#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct DependencyDepthMetrics {
33    pub max_depth: usize,
34    pub avg_depth: f64,
35}
36
37/// Layer-to-layer coupling matrix: counts of edges between each pair of layers.
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct LayerCouplingMatrix {
40    pub matrix: HashMap<String, HashMap<String, usize>>,
41}
42
43impl LayerCouplingMatrix {
44    pub fn new() -> Self {
45        let layers = [
46            ArchLayer::Domain,
47            ArchLayer::Application,
48            ArchLayer::Infrastructure,
49            ArchLayer::Presentation,
50        ];
51        let mut matrix = HashMap::new();
52        for from in &layers {
53            let mut row = HashMap::new();
54            for to in &layers {
55                row.insert(to.to_string(), 0);
56            }
57            matrix.insert(from.to_string(), row);
58        }
59        Self { matrix }
60    }
61
62    pub fn increment(&mut self, from: &ArchLayer, to: &ArchLayer) {
63        if let Some(row) = self.matrix.get_mut(&from.to_string()) {
64            if let Some(count) = row.get_mut(&to.to_string()) {
65                *count += 1;
66            }
67        }
68    }
69}
70
71impl Default for LayerCouplingMatrix {
72    fn default() -> Self {
73        Self::new()
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn test_coupling_matrix_increment() {
83        let mut matrix = LayerCouplingMatrix::new();
84        matrix.increment(&ArchLayer::Domain, &ArchLayer::Infrastructure);
85        matrix.increment(&ArchLayer::Domain, &ArchLayer::Infrastructure);
86        assert_eq!(
87            matrix.matrix["domain"]["infrastructure"], 2,
88            "should count two edges"
89        );
90    }
91}