Skip to main content

cargo_coupling/metrics/
coupling.rs

1use std::path::PathBuf;
2
3use crate::volatility::Volatility;
4
5use super::dimensions::{Distance, IntegrationStrength, Visibility};
6
7#[derive(Debug, Clone, Default)]
8pub struct CouplingLocation {
9    /// File path where the coupling originates
10    pub file_path: Option<PathBuf>,
11    /// Line number in the source file
12    pub line: usize,
13}
14
15/// Metrics for a single coupling relationship
16#[derive(Debug, Clone)]
17pub struct CouplingMetrics {
18    /// Source component
19    pub source: String,
20    /// Target component
21    pub target: String,
22    /// Integration strength
23    pub strength: IntegrationStrength,
24    /// Distance between components
25    pub distance: Distance,
26    /// Volatility of the target
27    pub volatility: Volatility,
28    /// Source crate name (when workspace analysis is available)
29    pub source_crate: Option<String>,
30    /// Target crate name (when workspace analysis is available)
31    pub target_crate: Option<String>,
32    /// Visibility of the target item (for intrusive detection)
33    pub target_visibility: Visibility,
34    /// Location where the coupling occurs
35    pub location: CouplingLocation,
36}
37
38impl CouplingMetrics {
39    /// Create new coupling metrics
40    pub fn new(
41        source: String,
42        target: String,
43        strength: IntegrationStrength,
44        distance: Distance,
45        volatility: Volatility,
46    ) -> Self {
47        Self {
48            source,
49            target,
50            strength,
51            distance,
52            volatility,
53            source_crate: None,
54            target_crate: None,
55            target_visibility: Visibility::default(),
56            location: CouplingLocation::default(),
57        }
58    }
59
60    /// Create new coupling metrics with visibility
61    pub fn with_visibility(
62        source: String,
63        target: String,
64        strength: IntegrationStrength,
65        distance: Distance,
66        volatility: Volatility,
67        visibility: Visibility,
68    ) -> Self {
69        Self {
70            source,
71            target,
72            strength,
73            distance,
74            volatility,
75            source_crate: None,
76            target_crate: None,
77            target_visibility: visibility,
78            location: CouplingLocation::default(),
79        }
80    }
81
82    /// Create new coupling metrics with location
83    #[allow(clippy::too_many_arguments)]
84    pub fn with_location(
85        source: String,
86        target: String,
87        strength: IntegrationStrength,
88        distance: Distance,
89        volatility: Volatility,
90        visibility: Visibility,
91        file_path: PathBuf,
92        line: usize,
93    ) -> Self {
94        Self {
95            source,
96            target,
97            strength,
98            distance,
99            volatility,
100            source_crate: None,
101            target_crate: None,
102            target_visibility: visibility,
103            location: CouplingLocation {
104                file_path: Some(file_path),
105                line,
106            },
107        }
108    }
109
110    /// Check if this coupling represents intrusive access based on visibility
111    ///
112    /// Returns true if the target's visibility suggests this is access to
113    /// internal implementation details rather than a public API.
114    pub fn is_visibility_intrusive(&self) -> bool {
115        let same_crate = self.source_crate == self.target_crate;
116        let same_module =
117            self.distance == Distance::SameModule || self.distance == Distance::SameFunction;
118        self.target_visibility
119            .is_intrusive_from(same_crate, same_module)
120    }
121
122    /// Get effective strength considering visibility
123    ///
124    /// If the target is not publicly visible and being accessed from outside,
125    /// the coupling is considered more intrusive.
126    pub fn effective_strength(&self) -> IntegrationStrength {
127        if self.is_visibility_intrusive() && self.strength != IntegrationStrength::Intrusive {
128            // Upgrade to more intrusive if accessing non-public items
129            match self.strength {
130                IntegrationStrength::Contract => IntegrationStrength::Model,
131                IntegrationStrength::Model => IntegrationStrength::Functional,
132                IntegrationStrength::Functional => IntegrationStrength::Intrusive,
133                IntegrationStrength::Intrusive => IntegrationStrength::Intrusive,
134            }
135        } else {
136            self.strength
137        }
138    }
139
140    /// Get effective strength value considering visibility
141    pub fn effective_strength_value(&self) -> f64 {
142        self.effective_strength().value()
143    }
144
145    /// Get numeric strength value
146    pub fn strength_value(&self) -> f64 {
147        self.strength.value()
148    }
149
150    /// Get numeric distance value
151    pub fn distance_value(&self) -> f64 {
152        self.distance.value()
153    }
154
155    /// Get numeric volatility value
156    pub fn volatility_value(&self) -> f64 {
157        self.volatility.value()
158    }
159}
160
161// ===== Module-Level Facts =====