1use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9#[serde(rename_all = "snake_case")]
10pub enum EdgeType {
11 Imports,
14
15 TypeRef,
18
19 Calls,
22
23 ComposedOf,
26
27 Implements,
30
31 Extends,
34
35 Exports,
37
38 GenericRef,
41
42 MacroUse,
44
45 Tests,
47
48 DocRef,
50}
51
52impl EdgeType {
53 pub fn is_strong(&self) -> bool {
55 matches!(
56 self,
57 EdgeType::Imports | EdgeType::TypeRef | EdgeType::Implements | EdgeType::Extends
58 )
59 }
60
61 pub fn is_weak(&self) -> bool {
63 matches!(
64 self,
65 EdgeType::Calls | EdgeType::DocRef | EdgeType::Tests | EdgeType::MacroUse
66 )
67 }
68
69 pub fn should_auto_expand(&self) -> bool {
71 matches!(
72 self,
73 EdgeType::Imports | EdgeType::TypeRef | EdgeType::GenericRef
74 )
75 }
76
77 pub fn assembly_priority(&self) -> u8 {
79 match self {
80 EdgeType::Imports => 1,
81 EdgeType::TypeRef => 2,
82 EdgeType::Implements => 3,
83 EdgeType::Extends => 3,
84 EdgeType::GenericRef => 4,
85 EdgeType::Calls => 5,
86 EdgeType::MacroUse => 6,
87 EdgeType::ComposedOf => 10,
88 EdgeType::Exports => 10,
89 EdgeType::Tests => 20,
90 EdgeType::DocRef => 20,
91 }
92 }
93}
94
95impl std::fmt::Display for EdgeType {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 let s = match self {
98 EdgeType::Imports => "imports",
99 EdgeType::TypeRef => "type_ref",
100 EdgeType::Calls => "calls",
101 EdgeType::ComposedOf => "composed_of",
102 EdgeType::Implements => "implements",
103 EdgeType::Extends => "extends",
104 EdgeType::Exports => "exports",
105 EdgeType::GenericRef => "generic_ref",
106 EdgeType::MacroUse => "macro_use",
107 EdgeType::Tests => "tests",
108 EdgeType::DocRef => "doc_ref",
109 };
110 write!(f, "{}", s)
111 }
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct Edge {
117 pub source: String,
119
120 pub target: String,
122
123 pub edge_type: EdgeType,
125
126 pub symbols: Vec<String>,
128
129 pub weight: f32,
131
132 pub metadata: Option<EdgeMetadata>,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct EdgeMetadata {
139 pub source_line: Option<usize>,
141
142 pub is_reexport: bool,
144
145 pub condition: Option<String>,
147}
148
149impl Edge {
150 pub fn new(
152 source: impl Into<String>,
153 target: impl Into<String>,
154 edge_type: EdgeType,
155 ) -> Self {
156 Self {
157 source: source.into(),
158 target: target.into(),
159 edge_type,
160 symbols: Vec::new(),
161 weight: 1.0,
162 metadata: None,
163 }
164 }
165
166 pub fn with_symbols(mut self, symbols: Vec<String>) -> Self {
168 self.symbols = symbols;
169 self
170 }
171
172 pub fn with_weight(mut self, weight: f32) -> Self {
174 self.weight = weight.clamp(0.0, 1.0);
175 self
176 }
177
178 pub fn with_metadata(mut self, metadata: EdgeMetadata) -> Self {
180 self.metadata = Some(metadata);
181 self
182 }
183
184 pub fn to_bytes(&self) -> Result<Vec<u8>, serde_json::Error> {
186 serde_json::to_vec(self)
187 }
188
189 pub fn from_bytes(bytes: &[u8]) -> Result<Self, serde_json::Error> {
191 serde_json::from_slice(bytes)
192 }
193}
194
195#[derive(Debug, Clone, Default, Serialize, Deserialize)]
197pub struct EdgeSet {
198 pub edges: Vec<Edge>,
199}
200
201impl EdgeSet {
202 pub fn new() -> Self {
203 Self { edges: Vec::new() }
204 }
205
206 pub fn add(&mut self, edge: Edge) {
207 self.edges.push(edge);
208 }
209
210 pub fn filter_by_type(&self, edge_type: EdgeType) -> impl Iterator<Item = &Edge> {
211 self.edges.iter().filter(move |e| e.edge_type == edge_type)
212 }
213
214 pub fn strong_edges(&self) -> impl Iterator<Item = &Edge> {
215 self.edges.iter().filter(|e| e.edge_type.is_strong())
216 }
217
218 pub fn weak_edges(&self) -> impl Iterator<Item = &Edge> {
219 self.edges.iter().filter(|e| e.edge_type.is_weak())
220 }
221
222 pub fn targets(&self) -> impl Iterator<Item = &String> {
223 self.edges.iter().map(|e| &e.target)
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230
231 #[test]
232 fn test_edge_creation() {
233 let edge = Edge::new("chunk:a", "chunk:b", EdgeType::Imports)
234 .with_symbols(vec!["foo".to_string(), "bar".to_string()])
235 .with_weight(0.8);
236
237 assert_eq!(edge.source, "chunk:a");
238 assert_eq!(edge.target, "chunk:b");
239 assert!(edge.edge_type.is_strong());
240 assert_eq!(edge.symbols.len(), 2);
241 }
242
243 #[test]
244 fn test_edge_priorities() {
245 assert!(EdgeType::Imports.assembly_priority() < EdgeType::Calls.assembly_priority());
246 assert!(EdgeType::TypeRef.assembly_priority() < EdgeType::Tests.assembly_priority());
247 }
248
249 #[test]
250 fn test_edge_set() {
251 let mut set = EdgeSet::new();
252 set.add(Edge::new("a", "b", EdgeType::Imports));
253 set.add(Edge::new("a", "c", EdgeType::Calls));
254 set.add(Edge::new("a", "d", EdgeType::TypeRef));
255
256 assert_eq!(set.strong_edges().count(), 2); assert_eq!(set.weak_edges().count(), 1); }
259}