borrowscope_runtime/
event.rs

1//! Event types for tracking ownership operations.
2//!
3//! This module defines the [`Event`] enum which represents all possible
4//! ownership and borrowing events that can be tracked at runtime.
5//!
6//! # Event Categories
7//!
8//! - **Basic ownership**: `New`, `Borrow`, `Move`, `Drop`
9//! - **Smart pointers**: `RcNew`, `RcClone`, `ArcNew`, `ArcClone`
10//! - **Interior mutability**: `RefCellNew`, `RefCellBorrow`, `RefCellDrop`, `CellNew`, `CellGet`, `CellSet`
11//! - **Unsafe operations**: `RawPtrCreated`, `RawPtrDeref`, `UnsafeBlockEnter`, `UnsafeBlockExit`
12//!
13//! # Serialization
14//!
15//! All events serialize to JSON with a `type` tag for easy filtering.
16
17use serde::{Deserialize, Serialize};
18
19/// An ownership or borrowing event recorded at runtime.
20#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
21#[serde(tag = "type")]
22pub enum Event {
23    /// Variable created via [`track_new`](crate::track_new).
24    New {
25        timestamp: u64,
26        var_name: String,
27        var_id: String,
28        type_name: String,
29    },
30
31    /// Variable borrowed via [`track_borrow`](crate::track_borrow).
32    Borrow {
33        timestamp: u64,
34        borrower_name: String,
35        borrower_id: String,
36        owner_id: String,
37        mutable: bool,
38    },
39
40    /// Ownership moved via [`track_move`](crate::track_move).
41    Move {
42        timestamp: u64,
43        from_id: String,
44        to_name: String,
45        to_id: String,
46    },
47
48    /// Variable dropped via [`track_drop`](crate::track_drop).
49    Drop { timestamp: u64, var_id: String },
50
51    /// `Rc::new` allocation with reference counting.
52    RcNew {
53        timestamp: u64,
54        var_name: String,
55        var_id: String,
56        type_name: String,
57        strong_count: usize,
58        weak_count: usize,
59    },
60
61    /// `Rc::clone` operation (shared ownership).
62    RcClone {
63        timestamp: u64,
64        var_name: String,
65        var_id: String,
66        source_id: String,
67        strong_count: usize,
68        weak_count: usize,
69    },
70
71    /// Arc::new allocation with atomic reference counting
72    ArcNew {
73        timestamp: u64,
74        var_name: String,
75        var_id: String,
76        type_name: String,
77        strong_count: usize,
78        weak_count: usize,
79    },
80
81    /// Arc::clone operation (thread-safe shared ownership)
82    ArcClone {
83        timestamp: u64,
84        var_name: String,
85        var_id: String,
86        source_id: String,
87        strong_count: usize,
88        weak_count: usize,
89    },
90
91    /// RefCell::new allocation
92    RefCellNew {
93        timestamp: u64,
94        var_name: String,
95        var_id: String,
96        type_name: String,
97    },
98
99    /// RefCell::borrow or borrow_mut operation
100    RefCellBorrow {
101        timestamp: u64,
102        borrow_id: String,
103        refcell_id: String,
104        is_mutable: bool,
105        location: String,
106    },
107
108    /// RefCell borrow dropped (Ref/RefMut dropped)
109    RefCellDrop {
110        timestamp: u64,
111        borrow_id: String,
112        location: String,
113    },
114
115    /// Cell::new allocation
116    CellNew {
117        timestamp: u64,
118        var_name: String,
119        var_id: String,
120        type_name: String,
121    },
122
123    /// Cell::get operation
124    CellGet {
125        timestamp: u64,
126        cell_id: String,
127        location: String,
128    },
129
130    /// Cell::set operation
131    CellSet {
132        timestamp: u64,
133        cell_id: String,
134        location: String,
135    },
136
137    /// Static variable initialization
138    StaticInit {
139        timestamp: u64,
140        var_name: String,
141        var_id: String,
142        type_name: String,
143        is_mutable: bool,
144    },
145
146    /// Static variable access (read or write)
147    StaticAccess {
148        timestamp: u64,
149        var_id: String,
150        var_name: String,
151        is_write: bool,
152        location: String,
153    },
154
155    /// Const evaluation (compile-time constant)
156    ConstEval {
157        timestamp: u64,
158        const_name: String,
159        const_id: String,
160        type_name: String,
161        location: String,
162    },
163
164    /// Raw pointer created
165    RawPtrCreated {
166        timestamp: u64,
167        var_name: String,
168        var_id: String,
169        ptr_type: String,
170        address: usize,
171        location: String,
172    },
173
174    /// Raw pointer dereferenced
175    RawPtrDeref {
176        timestamp: u64,
177        ptr_id: String,
178        location: String,
179        is_write: bool,
180    },
181
182    /// Unsafe block entered
183    UnsafeBlockEnter {
184        timestamp: u64,
185        block_id: String,
186        location: String,
187    },
188
189    /// Unsafe block exited
190    UnsafeBlockExit {
191        timestamp: u64,
192        block_id: String,
193        location: String,
194    },
195
196    /// Unsafe function called
197    UnsafeFnCall {
198        timestamp: u64,
199        fn_name: String,
200        location: String,
201    },
202
203    /// FFI (Foreign Function Interface) call
204    FfiCall {
205        timestamp: u64,
206        fn_name: String,
207        location: String,
208    },
209
210    /// Transmute operation
211    Transmute {
212        timestamp: u64,
213        from_type: String,
214        to_type: String,
215        location: String,
216    },
217
218    /// Union field access
219    UnionFieldAccess {
220        timestamp: u64,
221        union_name: String,
222        field_name: String,
223        location: String,
224    },
225}
226
227impl Event {
228    /// Get the timestamp of this event
229    pub fn timestamp(&self) -> u64 {
230        match self {
231            Event::New { timestamp, .. }
232            | Event::Borrow { timestamp, .. }
233            | Event::Move { timestamp, .. }
234            | Event::Drop { timestamp, .. }
235            | Event::RcNew { timestamp, .. }
236            | Event::RcClone { timestamp, .. }
237            | Event::ArcNew { timestamp, .. }
238            | Event::ArcClone { timestamp, .. }
239            | Event::RefCellNew { timestamp, .. }
240            | Event::RefCellBorrow { timestamp, .. }
241            | Event::RefCellDrop { timestamp, .. }
242            | Event::CellNew { timestamp, .. }
243            | Event::CellGet { timestamp, .. }
244            | Event::CellSet { timestamp, .. }
245            | Event::StaticInit { timestamp, .. }
246            | Event::StaticAccess { timestamp, .. }
247            | Event::ConstEval { timestamp, .. }
248            | Event::RawPtrCreated { timestamp, .. }
249            | Event::RawPtrDeref { timestamp, .. }
250            | Event::UnsafeBlockEnter { timestamp, .. }
251            | Event::UnsafeBlockExit { timestamp, .. }
252            | Event::UnsafeFnCall { timestamp, .. }
253            | Event::FfiCall { timestamp, .. }
254            | Event::Transmute { timestamp, .. }
255            | Event::UnionFieldAccess { timestamp, .. } => *timestamp,
256        }
257    }
258
259    /// Get the variable name (if applicable)
260    pub fn var_name(&self) -> Option<&str> {
261        match self {
262            Event::New { var_name, .. }
263            | Event::RcNew { var_name, .. }
264            | Event::RcClone { var_name, .. }
265            | Event::ArcNew { var_name, .. }
266            | Event::ArcClone { var_name, .. }
267            | Event::RefCellNew { var_name, .. }
268            | Event::CellNew { var_name, .. }
269            | Event::StaticInit { var_name, .. }
270            | Event::StaticAccess { var_name, .. }
271            | Event::RawPtrCreated { var_name, .. }
272            | Event::ConstEval {
273                const_name: var_name,
274                ..
275            } => Some(var_name),
276            Event::Borrow { borrower_name, .. } => Some(borrower_name),
277            Event::Move { to_name, .. } => Some(to_name),
278            Event::Drop { var_id, .. } => Some(var_id),
279            Event::RefCellBorrow { .. }
280            | Event::RefCellDrop { .. }
281            | Event::CellGet { .. }
282            | Event::CellSet { .. }
283            | Event::RawPtrDeref { .. }
284            | Event::UnsafeBlockEnter { .. }
285            | Event::UnsafeBlockExit { .. }
286            | Event::UnsafeFnCall { .. }
287            | Event::FfiCall { .. }
288            | Event::Transmute { .. }
289            | Event::UnionFieldAccess { .. } => None,
290        }
291    }
292
293    /// Check if this is a New event
294    pub fn is_new(&self) -> bool {
295        matches!(self, Event::New { .. })
296    }
297
298    /// Check if this is a Borrow event
299    pub fn is_borrow(&self) -> bool {
300        matches!(self, Event::Borrow { .. })
301    }
302
303    /// Check if this is a Move event
304    pub fn is_move(&self) -> bool {
305        matches!(self, Event::Move { .. })
306    }
307
308    /// Check if this is a Drop event
309    pub fn is_drop(&self) -> bool {
310        matches!(self, Event::Drop { .. })
311    }
312
313    /// Check if this is an Rc event (new or clone)
314    pub fn is_rc(&self) -> bool {
315        matches!(self, Event::RcNew { .. } | Event::RcClone { .. })
316    }
317
318    /// Check if this is an Arc event (new or clone)
319    pub fn is_arc(&self) -> bool {
320        matches!(self, Event::ArcNew { .. } | Event::ArcClone { .. })
321    }
322
323    /// Check if this is a reference-counted event
324    pub fn is_refcounted(&self) -> bool {
325        self.is_rc() || self.is_arc()
326    }
327
328    /// Check if this is a RefCell event
329    pub fn is_refcell(&self) -> bool {
330        matches!(
331            self,
332            Event::RefCellNew { .. } | Event::RefCellBorrow { .. } | Event::RefCellDrop { .. }
333        )
334    }
335
336    /// Check if this is a Cell event
337    pub fn is_cell(&self) -> bool {
338        matches!(
339            self,
340            Event::CellNew { .. } | Event::CellGet { .. } | Event::CellSet { .. }
341        )
342    }
343
344    /// Check if this is an interior mutability event
345    pub fn is_interior_mutability(&self) -> bool {
346        self.is_refcell() || self.is_cell()
347    }
348
349    /// Check if this is a static event
350    pub fn is_static(&self) -> bool {
351        matches!(self, Event::StaticInit { .. } | Event::StaticAccess { .. })
352    }
353
354    /// Check if this is a const event
355    pub fn is_const(&self) -> bool {
356        matches!(self, Event::ConstEval { .. })
357    }
358
359    /// Check if this is a global variable event (static or const)
360    pub fn is_global(&self) -> bool {
361        self.is_static() || self.is_const()
362    }
363
364    /// Check if this is an unsafe event
365    pub fn is_unsafe(&self) -> bool {
366        matches!(
367            self,
368            Event::RawPtrCreated { .. }
369                | Event::RawPtrDeref { .. }
370                | Event::UnsafeBlockEnter { .. }
371                | Event::UnsafeBlockExit { .. }
372                | Event::UnsafeFnCall { .. }
373                | Event::FfiCall { .. }
374                | Event::Transmute { .. }
375                | Event::UnionFieldAccess { .. }
376        )
377    }
378
379    /// Check if this is a raw pointer event
380    pub fn is_raw_ptr(&self) -> bool {
381        matches!(
382            self,
383            Event::RawPtrCreated { .. } | Event::RawPtrDeref { .. }
384        )
385    }
386
387    /// Check if this is an FFI event
388    pub fn is_ffi(&self) -> bool {
389        matches!(self, Event::FfiCall { .. })
390    }
391
392    /// Get strong count if this is a reference-counted event
393    pub fn strong_count(&self) -> Option<usize> {
394        match self {
395            Event::RcNew { strong_count, .. }
396            | Event::RcClone { strong_count, .. }
397            | Event::ArcNew { strong_count, .. }
398            | Event::ArcClone { strong_count, .. } => Some(*strong_count),
399            _ => None,
400        }
401    }
402
403    /// Get weak count if this is a reference-counted event
404    pub fn weak_count(&self) -> Option<usize> {
405        match self {
406            Event::RcNew { weak_count, .. }
407            | Event::RcClone { weak_count, .. }
408            | Event::ArcNew { weak_count, .. }
409            | Event::ArcClone { weak_count, .. } => Some(*weak_count),
410            _ => None,
411        }
412    }
413}
414
415#[cfg(test)]
416mod tests {
417    use super::*;
418
419    #[test]
420    fn test_event_new() {
421        let event = Event::New {
422            timestamp: 1,
423            var_name: "x".to_string(),
424            var_id: "x_0".to_string(),
425            type_name: "i32".to_string(),
426        };
427
428        assert_eq!(event.timestamp(), 1);
429        assert_eq!(event.var_name(), Some("x"));
430        assert!(event.is_new());
431        assert!(!event.is_borrow());
432        assert!(!event.is_move());
433        assert!(!event.is_drop());
434    }
435
436    #[test]
437    fn test_event_borrow() {
438        let event = Event::Borrow {
439            timestamp: 2,
440            borrower_name: "r".to_string(),
441            borrower_id: "r_1".to_string(),
442            owner_id: "x_0".to_string(),
443            mutable: false,
444        };
445
446        assert_eq!(event.timestamp(), 2);
447        assert_eq!(event.var_name(), Some("r"));
448        assert!(event.is_borrow());
449        assert!(!event.is_new());
450    }
451
452    #[test]
453    fn test_event_move() {
454        let event = Event::Move {
455            timestamp: 3,
456            from_id: "x_0".to_string(),
457            to_name: "y".to_string(),
458            to_id: "y_1".to_string(),
459        };
460
461        assert_eq!(event.timestamp(), 3);
462        assert_eq!(event.var_name(), Some("y"));
463        assert!(event.is_move());
464    }
465
466    #[test]
467    fn test_event_drop() {
468        let event = Event::Drop {
469            timestamp: 4,
470            var_id: "x_0".to_string(),
471        };
472
473        assert_eq!(event.timestamp(), 4);
474        assert!(event.is_drop());
475    }
476
477    #[test]
478    fn test_event_serialization() {
479        let event = Event::New {
480            timestamp: 1,
481            var_name: "x".to_string(),
482            var_id: "x_0".to_string(),
483            type_name: "i32".to_string(),
484        };
485
486        let json = serde_json::to_string(&event).unwrap();
487        let deserialized: Event = serde_json::from_str(&json).unwrap();
488
489        assert_eq!(event, deserialized);
490    }
491
492    #[test]
493    fn test_borrow_mutable_flag() {
494        let immut = Event::Borrow {
495            timestamp: 1,
496            borrower_name: "r".to_string(),
497            borrower_id: "r_0".to_string(),
498            owner_id: "x_0".to_string(),
499            mutable: false,
500        };
501
502        let mut_borrow = Event::Borrow {
503            timestamp: 2,
504            borrower_name: "r".to_string(),
505            borrower_id: "r_1".to_string(),
506            owner_id: "x_0".to_string(),
507            mutable: true,
508        };
509
510        assert_ne!(immut, mut_borrow);
511    }
512}