borrowscope_runtime/tracker/
core.rs

1//! Core ownership tracking: new, borrow, move, drop
2
3use super::TRACKER;
4
5///
6/// # Arguments
7///
8/// * `name` - A descriptive name for the variable (used in event output)
9/// * `value` - The value being tracked (returned unchanged)
10///
11/// # Returns
12///
13/// The input `value`, unchanged. This allows chaining:
14/// ```rust
15/// # use borrowscope_runtime::*;
16/// # reset();
17/// let x = track_new("x", 42);
18/// assert_eq!(x, 42);
19/// ```
20///
21/// # Examples
22///
23/// Basic usage:
24/// ```rust
25/// # use borrowscope_runtime::*;
26/// # reset();
27/// let data = track_new("data", vec![1, 2, 3]);
28/// let events = get_events();
29/// assert!(events[0].is_new());
30/// ```
31///
32/// With structs:
33/// ```rust
34/// # use borrowscope_runtime::*;
35/// # reset();
36/// struct Point { x: i32, y: i32 }
37/// let p = track_new("point", Point { x: 10, y: 20 });
38/// ```
39#[inline(always)]
40pub fn track_new<T>(
41    #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
42    value: T,
43) -> T {
44    #[cfg(feature = "track")]
45    {
46        let type_name = std::any::type_name::<T>();
47        let mut tracker = TRACKER.lock();
48        tracker.record_new(name, type_name);
49    }
50    value
51}
52
53/// Track an immutable borrow.
54///
55/// Records a `Borrow` event with `mutable: false` and returns the reference unchanged.
56/// Use this when creating a shared reference (`&T`).
57///
58/// # Arguments
59///
60/// * `name` - A descriptive name for the borrow
61/// * `value` - The reference being tracked (returned unchanged)
62///
63/// # Returns
64///
65/// The input reference, unchanged.
66///
67/// # Examples
68///
69/// ```rust
70/// # use borrowscope_runtime::*;
71/// # reset();
72/// let data = track_new("data", vec![1, 2, 3]);
73/// let r1 = track_borrow("r1", &data);
74/// let r2 = track_borrow("r2", &data); // Multiple immutable borrows OK
75/// println!("{:?}, {:?}", r1, r2);
76///
77/// let events = get_events();
78/// assert!(events[1].is_borrow());
79/// assert!(events[2].is_borrow());
80/// ```
81#[inline(always)]
82pub fn track_borrow<'a, T: ?Sized>(
83    #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
84    value: &'a T,
85) -> &'a T {
86    #[cfg(feature = "track")]
87    {
88        let mut tracker = TRACKER.lock();
89        tracker.record_borrow(name, "unknown", false);
90    }
91    value
92}
93
94/// Track a mutable borrow.
95///
96/// Records a `Borrow` event with `mutable: true` and returns the reference unchanged.
97/// Use this when creating an exclusive reference (`&mut T`).
98///
99/// # Arguments
100///
101/// * `name` - A descriptive name for the borrow
102/// * `value` - The mutable reference being tracked (returned unchanged)
103///
104/// # Returns
105///
106/// The input mutable reference, unchanged.
107///
108/// # Examples
109///
110/// ```rust
111/// # use borrowscope_runtime::*;
112/// # reset();
113/// let mut data = track_new("data", vec![1, 2, 3]);
114/// {
115///     let r = track_borrow_mut("r", &mut data);
116///     r.push(4);
117/// }
118/// // Mutable borrow ended, can borrow again
119/// let events = get_events();
120/// assert!(events[1].is_borrow());
121/// ```
122#[inline(always)]
123pub fn track_borrow_mut<'a, T: ?Sized>(
124    #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
125    value: &'a mut T,
126) -> &'a mut T {
127    #[cfg(feature = "track")]
128    {
129        let mut tracker = TRACKER.lock();
130        tracker.record_borrow(name, "unknown", true);
131    }
132    value
133}
134
135/// Track an ownership move.
136///
137/// Records a `Move` event and returns the value unchanged.
138/// Use this when ownership transfers from one variable to another.
139///
140/// # Arguments
141///
142/// * `from_name` - Name of the source variable (giving up ownership)
143/// * `to_name` - Name of the destination variable (receiving ownership)
144/// * `value` - The value being moved (returned unchanged)
145///
146/// # Returns
147///
148/// The input `value`, unchanged.
149///
150/// # Examples
151///
152/// ```rust
153/// # use borrowscope_runtime::*;
154/// # reset();
155/// let s1 = track_new("s1", String::from("hello"));
156/// let s2 = track_move("s1", "s2", s1);
157/// // s1 is no longer valid, s2 owns the String
158///
159/// let events = get_events();
160/// assert!(events[1].is_move());
161/// ```
162#[inline(always)]
163pub fn track_move<T>(
164    #[cfg_attr(not(feature = "track"), allow(unused_variables))] from_name: &str,
165    #[cfg_attr(not(feature = "track"), allow(unused_variables))] to_name: &str,
166    value: T,
167) -> T {
168    #[cfg(feature = "track")]
169    {
170        let mut tracker = TRACKER.lock();
171        tracker.record_move(from_name, to_name);
172    }
173    value
174}
175
176/// Track a variable going out of scope.
177///
178/// Records a `Drop` event. Call this when a variable's lifetime ends.
179/// Unlike other tracking functions, this doesn't return a value since
180/// the variable is being destroyed.
181///
182/// # Arguments
183///
184/// * `name` - Name of the variable being dropped
185///
186/// # Examples
187///
188/// ```rust
189/// # use borrowscope_runtime::*;
190/// # reset();
191/// {
192///     let x = track_new("x", 42);
193///     // x goes out of scope here
194///     track_drop("x");
195/// }
196///
197/// let events = get_events();
198/// assert!(events[1].is_drop());
199/// ```
200///
201/// # Note
202///
203/// For automatic drop tracking, consider using RAII guards or the
204/// future `borrowscope-macro` crate which will instrument drops automatically.
205#[inline(always)]
206pub fn track_drop(#[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str) {
207    #[cfg(feature = "track")]
208    {
209        let mut tracker = TRACKER.lock();
210        tracker.record_drop(name);
211    }
212}
213
214/// Track multiple drops in batch (optimized).
215///
216/// Records multiple `Drop` events efficiently with a single lock acquisition.
217/// Use this when multiple variables go out of scope simultaneously.
218///
219/// # Arguments
220///
221/// * `names` - Slice of variable names being dropped
222///
223/// # Examples
224///
225/// ```rust
226/// # use borrowscope_runtime::*;
227/// # reset();
228/// let a = track_new("a", 1);
229/// let b = track_new("b", 2);
230/// let c = track_new("c", 3);
231/// // All go out of scope together
232/// track_drop_batch(&["a", "b", "c"]);
233///
234/// let events = get_events();
235/// assert_eq!(events.len(), 6); // 3 New + 3 Drop
236/// ```
237#[inline(always)]
238pub fn track_drop_batch(
239    #[cfg_attr(not(feature = "track"), allow(unused_variables))] names: &[&str],
240) {
241    #[cfg(feature = "track")]
242    {
243        let mut tracker = TRACKER.lock();
244        for &name in names {
245            tracker.record_drop(name);
246        }
247    }
248}
249
250/// Reset tracking state.
251///
252/// Clears all recorded events and resets internal counters.
253/// Call this before starting a new tracking session.
254///
255/// # Examples
256///
257/// ```rust
258/// # use borrowscope_runtime::*;
259/// let _ = track_new("x", 1);
260/// assert!(!get_events().is_empty());
261///
262/// reset();
263/// assert!(get_events().is_empty());
264/// ```
265///
266/// # Thread Safety
267///
268/// This function is thread-safe but will clear events from all threads.
269/// In multi-threaded tests, use synchronization to ensure reset completes
270/// before other threads start tracking.
271pub fn __track_new_with_id_helper<T>(
272    #[cfg_attr(not(feature = "track"), allow(unused_variables))] id: usize,
273    #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
274    #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
275    value: T,
276) -> T {
277    #[cfg(feature = "track")]
278    {
279        let type_name = std::any::type_name::<T>();
280        let mut tracker = TRACKER.lock();
281        tracker.record_new_with_id(id, name, type_name, location);
282    }
283    value
284}
285
286/// Track a new variable with explicit ID and location (advanced API)
287#[inline(always)]
288pub fn track_new_with_id<T>(
289    #[cfg_attr(not(feature = "track"), allow(unused_variables))] id: usize,
290    #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
291    #[cfg_attr(not(feature = "track"), allow(unused_variables))] type_name: &str,
292    #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
293    value: T,
294) -> T {
295    #[cfg(feature = "track")]
296    {
297        let mut tracker = TRACKER.lock();
298        tracker.record_new_with_id(id, name, type_name, location);
299    }
300    value
301}
302
303/// Track an immutable borrow with full metadata (advanced API)
304#[inline(always)]
305pub fn track_borrow_with_id<'a, T: ?Sized>(
306    #[cfg_attr(not(feature = "track"), allow(unused_variables))] borrower_id: usize,
307    #[cfg_attr(not(feature = "track"), allow(unused_variables))] owner_id: usize,
308    #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
309    #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
310    #[cfg_attr(not(feature = "track"), allow(unused_variables))] mutable: bool,
311    value: &'a T,
312) -> &'a T {
313    #[cfg(feature = "track")]
314    {
315        let mut tracker = TRACKER.lock();
316        tracker.record_borrow_with_id(borrower_id, owner_id, name, location, mutable);
317    }
318    value
319}
320
321/// Track a mutable borrow with full metadata (advanced API)
322#[inline(always)]
323pub fn track_borrow_mut_with_id<'a, T: ?Sized>(
324    #[cfg_attr(not(feature = "track"), allow(unused_variables))] borrower_id: usize,
325    #[cfg_attr(not(feature = "track"), allow(unused_variables))] owner_id: usize,
326    #[cfg_attr(not(feature = "track"), allow(unused_variables))] name: &str,
327    #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
328    value: &'a mut T,
329) -> &'a mut T {
330    #[cfg(feature = "track")]
331    {
332        let mut tracker = TRACKER.lock();
333        tracker.record_borrow_with_id(borrower_id, owner_id, name, location, true);
334    }
335    value
336}
337
338/// Track a move with explicit IDs and location (advanced API)
339#[inline(always)]
340pub fn track_move_with_id<T>(
341    #[cfg_attr(not(feature = "track"), allow(unused_variables))] from_id: usize,
342    #[cfg_attr(not(feature = "track"), allow(unused_variables))] to_id: usize,
343    #[cfg_attr(not(feature = "track"), allow(unused_variables))] to_name: &str,
344    #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
345    value: T,
346) -> T {
347    #[cfg(feature = "track")]
348    {
349        let mut tracker = TRACKER.lock();
350        tracker.record_move_with_id(from_id, to_id, to_name, location);
351    }
352    value
353}
354
355/// Track a drop with explicit ID and location (advanced API)
356#[inline(always)]
357pub fn track_drop_with_id(
358    #[cfg_attr(not(feature = "track"), allow(unused_variables))] id: usize,
359    #[cfg_attr(not(feature = "track"), allow(unused_variables))] location: &str,
360) {
361    #[cfg(feature = "track")]
362    {
363        let mut tracker = TRACKER.lock();
364        tracker.record_drop_with_id(id, location);
365    }
366}
367