1#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize)]
20#[non_exhaustive]
21pub enum EdgeKind {
22 Ownership,
24
25 Import,
27
28 Reference,
30
31 Flow,
33}
34
35pub const CONFIDENCE_OWN_OR_DIRECT: f32 = 1.0;
40pub const CONFIDENCE_TRANSITIVE: f32 = 0.8;
42pub const CONFIDENCE_CROSS_LANGUAGE: f32 = 0.6;
44
45pub const CONFIDENCE_CLIENT_UNIQUE_LOAD: f32 = 1.0;
51pub const CONFIDENCE_CLIENT_MULTI_LOAD: f32 = 0.8;
53pub const CONFIDENCE_CLIENT_UNIQUE_GLOBAL: f32 = 0.6;
55pub const CONFIDENCE_CLIENT_MULTI_GLOBAL: f32 = 0.5;
57pub const CONFIDENCE_COMPUTED: f32 = 0.4;
59
60pub const CONFIDENCE_DEF_USE: f32 = 0.9;
62
63impl EdgeKind {
64 pub fn participates_in_scc(self) -> bool {
66 matches!(self, EdgeKind::Import | EdgeKind::Reference)
67 }
68
69 pub fn is_cross_file(self) -> bool {
71 matches!(self, EdgeKind::Import)
72 }
73
74 pub const fn as_str(self) -> &'static str {
76 match self {
77 EdgeKind::Ownership => "ownership",
78 EdgeKind::Import => "import",
79 EdgeKind::Reference => "reference",
80 EdgeKind::Flow => "flow",
81 }
82 }
83}
84
85impl std::fmt::Display for EdgeKind {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 write!(f, "{}", self.as_str())
88 }
89}
90
91#[derive(Debug, Clone, Copy, serde::Serialize)]
93pub struct EdgeData {
94 pub kind: EdgeKind,
96
97 pub confidence: f32,
100
101 pub flow_kind: Option<crate::model::FlowKind>,
103}
104
105impl EdgeData {
106 pub fn new(kind: EdgeKind) -> Self {
108 Self {
109 kind,
110 confidence: 1.0,
111 flow_kind: None,
112 }
113 }
114
115 pub fn with_confidence(kind: EdgeKind, confidence: f32) -> Self {
117 Self {
118 kind,
119 confidence: confidence.clamp(0.0, 1.0),
120 flow_kind: None,
121 }
122 }
123
124 pub fn flow(flow_kind: crate::model::FlowKind, confidence: f32) -> Self {
126 Self {
127 kind: EdgeKind::Flow,
128 confidence: confidence.clamp(0.0, 1.0),
129 flow_kind: Some(flow_kind),
130 }
131 }
132
133 pub fn participates_in_scc(&self) -> bool {
134 self.kind.participates_in_scc()
135 }
136}
137
138impl Default for EdgeData {
139 fn default() -> Self {
140 Self::new(EdgeKind::Reference)
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn edge_kind_scc_participation() {
150 assert!(!EdgeKind::Ownership.participates_in_scc());
151 assert!(EdgeKind::Import.participates_in_scc());
152 assert!(EdgeKind::Reference.participates_in_scc());
153 }
154
155 #[test]
156 fn edge_kind_as_str() {
157 assert_eq!(EdgeKind::Ownership.as_str(), "ownership");
158 assert_eq!(EdgeKind::Import.as_str(), "import");
159 assert_eq!(EdgeKind::Reference.as_str(), "reference");
160 assert_eq!(EdgeKind::Flow.as_str(), "flow");
161 }
162
163 #[test]
164 fn flow_edge_excluded_from_scc() {
165 assert!(!EdgeKind::Flow.participates_in_scc());
166 }
167
168 #[test]
169 fn flow_edge_not_cross_file() {
170 assert!(!EdgeKind::Flow.is_cross_file());
171 }
172
173 #[test]
174 fn edge_kind_display() {
175 assert_eq!(format!("{}", EdgeKind::Import), "import");
176 }
177
178 #[test]
179 fn edge_data_new_defaults_to_full_confidence() {
180 let edge = EdgeData::new(EdgeKind::Import);
181 assert_eq!(edge.kind, EdgeKind::Import);
182 assert_eq!(edge.confidence, 1.0);
183 assert!(edge.participates_in_scc());
184 }
185
186 #[test]
187 fn edge_data_with_confidence_clamps() {
188 let low = EdgeData::with_confidence(EdgeKind::Reference, -0.5);
189 assert_eq!(low.confidence, 0.0);
190
191 let high = EdgeData::with_confidence(EdgeKind::Reference, 1.5);
192 assert_eq!(high.confidence, 1.0);
193
194 let mid = EdgeData::with_confidence(EdgeKind::Reference, 0.75);
195 assert_eq!(mid.confidence, 0.75);
196 }
197
198 #[test]
199 fn edge_data_default() {
200 let edge: EdgeData = Default::default();
201 assert_eq!(edge.confidence, 1.0);
202 assert!(edge.participates_in_scc());
203 }
204}