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
35impl EdgeKind {
36 pub fn participates_in_scc(self) -> bool {
38 matches!(self, EdgeKind::Import | EdgeKind::Reference)
39 }
40
41 pub fn is_cross_file(self) -> bool {
43 matches!(self, EdgeKind::Import)
44 }
45
46 pub const fn as_str(self) -> &'static str {
48 match self {
49 EdgeKind::Ownership => "ownership",
50 EdgeKind::Import => "import",
51 EdgeKind::Reference => "reference",
52 EdgeKind::Flow => "flow",
53 }
54 }
55}
56
57impl std::fmt::Display for EdgeKind {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 write!(f, "{}", self.as_str())
60 }
61}
62
63#[derive(Debug, Clone, Copy, serde::Serialize)]
65pub struct EdgeData {
66 pub kind: EdgeKind,
68
69 pub confidence: f32,
72
73 pub flow_kind: Option<crate::model::FlowKind>,
75}
76
77impl EdgeData {
78 pub fn new(kind: EdgeKind) -> Self {
80 Self {
81 kind,
82 confidence: 1.0,
83 flow_kind: None,
84 }
85 }
86
87 pub fn with_confidence(kind: EdgeKind, confidence: f32) -> Self {
89 Self {
90 kind,
91 confidence: confidence.clamp(0.0, 1.0),
92 flow_kind: None,
93 }
94 }
95
96 pub fn flow(flow_kind: crate::model::FlowKind, confidence: f32) -> Self {
98 Self {
99 kind: EdgeKind::Flow,
100 confidence: confidence.clamp(0.0, 1.0),
101 flow_kind: Some(flow_kind),
102 }
103 }
104
105 pub fn participates_in_scc(&self) -> bool {
106 self.kind.participates_in_scc()
107 }
108}
109
110impl Default for EdgeData {
111 fn default() -> Self {
112 Self::new(EdgeKind::Reference)
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn edge_kind_scc_participation() {
122 assert!(!EdgeKind::Ownership.participates_in_scc());
123 assert!(EdgeKind::Import.participates_in_scc());
124 assert!(EdgeKind::Reference.participates_in_scc());
125 }
126
127 #[test]
128 fn edge_kind_as_str() {
129 assert_eq!(EdgeKind::Ownership.as_str(), "ownership");
130 assert_eq!(EdgeKind::Import.as_str(), "import");
131 assert_eq!(EdgeKind::Reference.as_str(), "reference");
132 assert_eq!(EdgeKind::Flow.as_str(), "flow");
133 }
134
135 #[test]
136 fn flow_edge_excluded_from_scc() {
137 assert!(!EdgeKind::Flow.participates_in_scc());
138 }
139
140 #[test]
141 fn flow_edge_not_cross_file() {
142 assert!(!EdgeKind::Flow.is_cross_file());
143 }
144
145 #[test]
146 fn edge_kind_display() {
147 assert_eq!(format!("{}", EdgeKind::Import), "import");
148 }
149
150 #[test]
151 fn edge_data_new_defaults_to_full_confidence() {
152 let edge = EdgeData::new(EdgeKind::Import);
153 assert_eq!(edge.kind, EdgeKind::Import);
154 assert_eq!(edge.confidence, 1.0);
155 assert!(edge.participates_in_scc());
156 }
157
158 #[test]
159 fn edge_data_with_confidence_clamps() {
160 let low = EdgeData::with_confidence(EdgeKind::Reference, -0.5);
161 assert_eq!(low.confidence, 0.0);
162
163 let high = EdgeData::with_confidence(EdgeKind::Reference, 1.5);
164 assert_eq!(high.confidence, 1.0);
165
166 let mid = EdgeData::with_confidence(EdgeKind::Reference, 0.75);
167 assert_eq!(mid.confidence, 0.75);
168 }
169
170 #[test]
171 fn edge_data_default() {
172 let edge: EdgeData = Default::default();
173 assert_eq!(edge.confidence, 1.0);
174 assert!(edge.participates_in_scc());
175 }
176}