1use crate::event::Event;
19use crate::lifetime::{LifetimeRelation, Timeline};
20use serde::{Deserialize, Serialize};
21use std::collections::HashMap;
22
23#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
25pub struct Variable {
26 pub id: String,
28 pub name: String,
30 pub type_name: String,
32 pub created_at: u64,
34 pub dropped_at: Option<u64>,
36}
37
38#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
40#[serde(tag = "kind")]
41pub enum Relationship {
42 Owns { from: String, to: String },
44 BorrowsImmut {
46 from: String,
47 to: String,
48 start: u64,
49 end: u64,
50 },
51 BorrowsMut {
53 from: String,
54 to: String,
55 start: u64,
56 end: u64,
57 },
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct OwnershipGraph {
63 pub nodes: Vec<Variable>,
65 pub edges: Vec<Relationship>,
67}
68
69impl OwnershipGraph {
70 pub fn new() -> Self {
72 Self {
73 nodes: Vec::new(),
74 edges: Vec::new(),
75 }
76 }
77
78 pub fn add_variable(&mut self, var: Variable) {
80 self.nodes.push(var);
81 }
82
83 pub fn add_relationship(&mut self, rel: Relationship) {
85 self.edges.push(rel);
86 }
87
88 pub fn find_variable(&self, id: &str) -> Option<&Variable> {
90 self.nodes.iter().find(|v| v.id == id)
91 }
92
93 pub fn find_borrows(&self, var_id: &str) -> Vec<&Relationship> {
95 self.edges
96 .iter()
97 .filter(|rel| match rel {
98 Relationship::BorrowsImmut { to, .. } | Relationship::BorrowsMut { to, .. } => {
99 to == var_id
100 }
101 _ => false,
102 })
103 .collect()
104 }
105
106 pub fn stats(&self) -> GraphStats {
108 let mut immut_borrows = 0;
109 let mut mut_borrows = 0;
110
111 for edge in &self.edges {
112 match edge {
113 Relationship::BorrowsImmut { .. } => immut_borrows += 1,
114 Relationship::BorrowsMut { .. } => mut_borrows += 1,
115 _ => {}
116 }
117 }
118
119 GraphStats {
120 total_variables: self.nodes.len(),
121 total_relationships: self.edges.len(),
122 immutable_borrows: immut_borrows,
123 mutable_borrows: mut_borrows,
124 }
125 }
126
127 pub fn lifetime_relations(&self, events: &[Event]) -> Vec<LifetimeRelation> {
129 Timeline::from_events(events).relations
130 }
131
132 pub fn create_timeline(&self, events: &[Event]) -> Timeline {
134 Timeline::from_events(events)
135 }
136
137 pub fn active_borrows_at(&self, events: &[Event], timestamp: u64) -> Vec<LifetimeRelation> {
139 let timeline = Timeline::from_events(events);
140 timeline.active_at(timestamp).into_iter().cloned().collect()
141 }
142
143 pub fn lifetimes_overlap(&self, events: &[Event], var1: &str, var2: &str) -> bool {
145 let timeline = Timeline::from_events(events);
146 timeline.lifetimes_overlap(var1, var2)
147 }
148}
149
150impl Default for OwnershipGraph {
151 fn default() -> Self {
152 Self::new()
153 }
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct GraphStats {
159 pub total_variables: usize,
160 pub total_relationships: usize,
161 pub immutable_borrows: usize,
162 pub mutable_borrows: usize,
163}
164
165pub fn build_graph(events: &[Event]) -> OwnershipGraph {
167 let mut graph = OwnershipGraph::new();
168 let mut var_map: HashMap<String, Variable> = HashMap::new();
169 let mut borrow_map: HashMap<String, (String, bool, u64)> = HashMap::new();
170
171 for event in events {
172 match event {
173 Event::New {
174 var_name,
175 var_id,
176 type_name,
177 timestamp,
178 } => {
179 let var = Variable {
180 id: var_id.clone(),
181 name: var_name.clone(),
182 type_name: type_name.clone(),
183 created_at: *timestamp,
184 dropped_at: None,
185 };
186 var_map.insert(var_id.clone(), var);
187 }
188
189 Event::Borrow {
190 borrower_id,
191 owner_id,
192 mutable,
193 timestamp,
194 ..
195 } => {
196 borrow_map.insert(
197 borrower_id.clone(),
198 (owner_id.clone(), *mutable, *timestamp),
199 );
200 }
201
202 Event::Drop { var_id, timestamp } => {
203 if let Some(var) = var_map.get_mut(var_id) {
205 var.dropped_at = Some(*timestamp);
206 }
207
208 if let Some((owner_id, is_mutable, start)) = borrow_map.remove(var_id) {
210 let rel = if is_mutable {
211 Relationship::BorrowsMut {
212 from: var_id.clone(),
213 to: owner_id,
214 start,
215 end: *timestamp,
216 }
217 } else {
218 Relationship::BorrowsImmut {
219 from: var_id.clone(),
220 to: owner_id,
221 start,
222 end: *timestamp,
223 }
224 };
225 graph.add_relationship(rel);
226 }
227 }
228
229 _ => {}
230 }
231 }
232
233 for var in var_map.into_values() {
235 graph.add_variable(var);
236 }
237
238 graph
239}
240
241#[cfg(test)]
242mod tests {
243 use super::*;
244
245 #[test]
246 fn test_empty_graph() {
247 let graph = OwnershipGraph::new();
248 assert_eq!(graph.nodes.len(), 0);
249 assert_eq!(graph.edges.len(), 0);
250 }
251
252 #[test]
253 fn test_add_variable() {
254 let mut graph = OwnershipGraph::new();
255
256 let var = Variable {
257 id: "x_0".to_string(),
258 name: "x".to_string(),
259 type_name: "i32".to_string(),
260 created_at: 1,
261 dropped_at: None,
262 };
263
264 graph.add_variable(var);
265 assert_eq!(graph.nodes.len(), 1);
266 }
267
268 #[test]
269 fn test_add_relationship() {
270 let mut graph = OwnershipGraph::new();
271
272 let rel = Relationship::BorrowsImmut {
273 from: "r_1".to_string(),
274 to: "x_0".to_string(),
275 start: 2,
276 end: 3,
277 };
278
279 graph.add_relationship(rel);
280 assert_eq!(graph.edges.len(), 1);
281 }
282
283 #[test]
284 fn test_find_variable() {
285 let mut graph = OwnershipGraph::new();
286
287 let var = Variable {
288 id: "x_0".to_string(),
289 name: "x".to_string(),
290 type_name: "i32".to_string(),
291 created_at: 1,
292 dropped_at: None,
293 };
294
295 graph.add_variable(var);
296
297 let found = graph.find_variable("x_0");
298 assert!(found.is_some());
299 assert_eq!(found.unwrap().name, "x");
300
301 let not_found = graph.find_variable("y_0");
302 assert!(not_found.is_none());
303 }
304
305 #[test]
306 fn test_find_borrows() {
307 let mut graph = OwnershipGraph::new();
308
309 graph.add_relationship(Relationship::BorrowsImmut {
310 from: "r_1".to_string(),
311 to: "x_0".to_string(),
312 start: 2,
313 end: 3,
314 });
315
316 graph.add_relationship(Relationship::BorrowsMut {
317 from: "s_2".to_string(),
318 to: "x_0".to_string(),
319 start: 4,
320 end: 5,
321 });
322
323 let borrows = graph.find_borrows("x_0");
324 assert_eq!(borrows.len(), 2);
325 }
326
327 #[test]
328 fn test_stats() {
329 let mut graph = OwnershipGraph::new();
330
331 graph.add_variable(Variable {
332 id: "x_0".to_string(),
333 name: "x".to_string(),
334 type_name: "i32".to_string(),
335 created_at: 1,
336 dropped_at: None,
337 });
338
339 graph.add_relationship(Relationship::BorrowsImmut {
340 from: "r_1".to_string(),
341 to: "x_0".to_string(),
342 start: 2,
343 end: 3,
344 });
345
346 graph.add_relationship(Relationship::BorrowsMut {
347 from: "s_2".to_string(),
348 to: "x_0".to_string(),
349 start: 4,
350 end: 5,
351 });
352
353 let stats = graph.stats();
354 assert_eq!(stats.total_variables, 1);
355 assert_eq!(stats.total_relationships, 2);
356 assert_eq!(stats.immutable_borrows, 1);
357 assert_eq!(stats.mutable_borrows, 1);
358 }
359
360 #[test]
361 fn test_build_graph_simple() {
362 let events = vec![
363 Event::New {
364 timestamp: 1,
365 var_name: "x".to_string(),
366 var_id: "x_0".to_string(),
367 type_name: "i32".to_string(),
368 },
369 Event::Drop {
370 timestamp: 2,
371 var_id: "x_0".to_string(),
372 },
373 ];
374
375 let graph = build_graph(&events);
376 assert_eq!(graph.nodes.len(), 1);
377 assert_eq!(graph.nodes[0].dropped_at, Some(2));
378 }
379
380 #[test]
381 fn test_build_graph_with_borrow() {
382 let events = vec![
383 Event::New {
384 timestamp: 1,
385 var_name: "x".to_string(),
386 var_id: "x_0".to_string(),
387 type_name: "i32".to_string(),
388 },
389 Event::Borrow {
390 timestamp: 2,
391 borrower_name: "r".to_string(),
392 borrower_id: "r_1".to_string(),
393 owner_id: "x_0".to_string(),
394 mutable: false,
395 },
396 Event::Drop {
397 timestamp: 3,
398 var_id: "r_1".to_string(),
399 },
400 Event::Drop {
401 timestamp: 4,
402 var_id: "x_0".to_string(),
403 },
404 ];
405
406 let graph = build_graph(&events);
407 assert_eq!(graph.nodes.len(), 1);
408 assert_eq!(graph.edges.len(), 1);
409
410 match &graph.edges[0] {
411 Relationship::BorrowsImmut {
412 from,
413 to,
414 start,
415 end,
416 } => {
417 assert_eq!(from, "r_1");
418 assert_eq!(to, "x_0");
419 assert_eq!(*start, 2);
420 assert_eq!(*end, 3);
421 }
422 _ => panic!("Expected BorrowsImmut"),
423 }
424 }
425
426 #[test]
427 fn test_build_graph_mutable_borrow() {
428 let events = vec![
429 Event::New {
430 timestamp: 1,
431 var_name: "x".to_string(),
432 var_id: "x_0".to_string(),
433 type_name: "Vec<i32>".to_string(),
434 },
435 Event::Borrow {
436 timestamp: 2,
437 borrower_name: "r".to_string(),
438 borrower_id: "r_1".to_string(),
439 owner_id: "x_0".to_string(),
440 mutable: true,
441 },
442 Event::Drop {
443 timestamp: 3,
444 var_id: "r_1".to_string(),
445 },
446 ];
447
448 let graph = build_graph(&events);
449 assert_eq!(graph.edges.len(), 1);
450
451 match &graph.edges[0] {
452 Relationship::BorrowsMut { .. } => {}
453 _ => panic!("Expected BorrowsMut"),
454 }
455 }
456
457 #[test]
458 fn test_serialization() {
459 let graph = OwnershipGraph {
460 nodes: vec![Variable {
461 id: "x_0".to_string(),
462 name: "x".to_string(),
463 type_name: "i32".to_string(),
464 created_at: 1,
465 dropped_at: Some(2),
466 }],
467 edges: vec![Relationship::BorrowsImmut {
468 from: "r_1".to_string(),
469 to: "x_0".to_string(),
470 start: 2,
471 end: 3,
472 }],
473 };
474
475 let json = serde_json::to_string(&graph).unwrap();
476 let deserialized: OwnershipGraph = serde_json::from_str(&json).unwrap();
477
478 assert_eq!(deserialized.nodes.len(), 1);
479 assert_eq!(deserialized.edges.len(), 1);
480 }
481}