borrowscope_runtime/
guard.rs

1//! RAII guards for automatic drop tracking.
2//!
3//! This module provides guard types that automatically call [`track_drop`](crate::track_drop)
4//! when they go out of scope, eliminating the need for manual drop tracking.
5//!
6//! # Example
7//!
8//! ```rust
9//! use borrowscope_runtime::*;
10//!
11//! reset();
12//! {
13//!     let _x = track_new_guard("x", 42);
14//!     // No need to call track_drop - happens automatically
15//! }
16//!
17//! let events = get_events();
18//! assert!(events.last().unwrap().is_drop());
19//! ```
20
21use crate::tracker::{track_drop, track_new};
22
23/// RAII guard that tracks drop automatically.
24///
25/// When this guard goes out of scope, it calls [`track_drop`] with the stored name.
26/// Use [`track_new_guard`] to create instances.
27///
28/// # Example
29///
30/// ```rust
31/// # use borrowscope_runtime::*;
32/// # reset();
33/// {
34///     let data = track_new_guard("data", vec![1, 2, 3]);
35///     println!("{:?}", *data); // Deref to access inner value
36/// } // track_drop("data") called here automatically
37///
38/// assert_eq!(get_events().len(), 2); // New + Drop
39/// ```
40pub struct TrackGuard<T> {
41    name: &'static str,
42    value: T,
43}
44
45impl<T> TrackGuard<T> {
46    /// Create a new tracking guard.
47    #[inline]
48    fn new(name: &'static str, value: T) -> Self {
49        Self { name, value }
50    }
51
52    /// Get the tracked name.
53    #[inline]
54    pub fn name(&self) -> &'static str {
55        self.name
56    }
57
58    /// Consume the guard and return the inner value without tracking drop.
59    ///
60    /// Use this when transferring ownership and you'll track the drop elsewhere.
61    #[inline]
62    pub fn into_inner(self) -> T {
63        let value = unsafe { std::ptr::read(&self.value) };
64        std::mem::forget(self);
65        value
66    }
67}
68
69impl<T> std::ops::Deref for TrackGuard<T> {
70    type Target = T;
71
72    #[inline]
73    fn deref(&self) -> &Self::Target {
74        &self.value
75    }
76}
77
78impl<T> std::ops::DerefMut for TrackGuard<T> {
79    #[inline]
80    fn deref_mut(&mut self) -> &mut Self::Target {
81        &mut self.value
82    }
83}
84
85impl<T> Drop for TrackGuard<T> {
86    #[inline]
87    fn drop(&mut self) {
88        track_drop(self.name);
89    }
90}
91
92/// Track a new variable with automatic drop tracking.
93///
94/// Returns a [`TrackGuard`] that automatically calls [`track_drop`] when it goes out of scope.
95///
96/// # Arguments
97///
98/// * `name` - A static string name for the variable (must be `'static` for the guard)
99/// * `value` - The value to track
100///
101/// # Returns
102///
103/// A `TrackGuard<T>` that derefs to `T` and tracks drop automatically.
104///
105/// # Example
106///
107/// ```rust
108/// # use borrowscope_runtime::*;
109/// # reset();
110/// fn example() {
111///     let data = track_new_guard("data", vec![1, 2, 3]);
112///     let sum: i32 = data.iter().sum();
113///     println!("Sum: {}", sum);
114///     // track_drop("data") called automatically here
115/// }
116///
117/// example();
118/// let events = get_events();
119/// assert!(events[0].is_new());
120/// assert!(events[1].is_drop());
121/// ```
122#[inline]
123pub fn track_new_guard<T>(name: &'static str, value: T) -> TrackGuard<T> {
124    let tracked = track_new(name, value);
125    TrackGuard::new(name, tracked)
126}
127
128/// RAII guard for borrow tracking (immutable).
129///
130/// Tracks when the borrow ends by calling [`track_drop`] on drop.
131pub struct BorrowGuard<'a, T: ?Sized> {
132    name: &'static str,
133    value: &'a T,
134}
135
136impl<'a, T: ?Sized> BorrowGuard<'a, T> {
137    #[inline]
138    fn new(name: &'static str, value: &'a T) -> Self {
139        Self { name, value }
140    }
141}
142
143impl<'a, T: ?Sized> std::ops::Deref for BorrowGuard<'a, T> {
144    type Target = T;
145
146    #[inline]
147    fn deref(&self) -> &Self::Target {
148        self.value
149    }
150}
151
152impl<'a, T: ?Sized> Drop for BorrowGuard<'a, T> {
153    #[inline]
154    fn drop(&mut self) {
155        track_drop(self.name);
156    }
157}
158
159/// Track an immutable borrow with automatic drop tracking.
160///
161/// # Example
162///
163/// ```rust
164/// # use borrowscope_runtime::*;
165/// # reset();
166/// let data = track_new_guard("data", vec![1, 2, 3]);
167/// {
168///     let r = track_borrow_guard("r", &*data);
169///     println!("{:?}", *r);
170/// } // track_drop("r") called automatically
171/// ```
172#[inline]
173pub fn track_borrow_guard<'a, T: ?Sized>(name: &'static str, value: &'a T) -> BorrowGuard<'a, T> {
174    let tracked = crate::track_borrow(name, value);
175    BorrowGuard::new(name, tracked)
176}
177
178/// RAII guard for mutable borrow tracking.
179pub struct BorrowMutGuard<'a, T: ?Sized> {
180    name: &'static str,
181    value: &'a mut T,
182}
183
184impl<'a, T: ?Sized> BorrowMutGuard<'a, T> {
185    #[inline]
186    fn new(name: &'static str, value: &'a mut T) -> Self {
187        Self { name, value }
188    }
189}
190
191impl<'a, T: ?Sized> std::ops::Deref for BorrowMutGuard<'a, T> {
192    type Target = T;
193
194    #[inline]
195    fn deref(&self) -> &Self::Target {
196        self.value
197    }
198}
199
200impl<'a, T: ?Sized> std::ops::DerefMut for BorrowMutGuard<'a, T> {
201    #[inline]
202    fn deref_mut(&mut self) -> &mut Self::Target {
203        self.value
204    }
205}
206
207impl<'a, T: ?Sized> Drop for BorrowMutGuard<'a, T> {
208    #[inline]
209    fn drop(&mut self) {
210        track_drop(self.name);
211    }
212}
213
214/// Track a mutable borrow with automatic drop tracking.
215///
216/// # Example
217///
218/// ```rust
219/// # use borrowscope_runtime::*;
220/// # reset();
221/// let mut data = track_new_guard("data", vec![1, 2, 3]);
222/// {
223///     let mut r = track_borrow_mut_guard("r", &mut *data);
224///     r.push(4);
225/// } // track_drop("r") called automatically
226/// ```
227#[inline]
228pub fn track_borrow_mut_guard<'a, T: ?Sized>(
229    name: &'static str,
230    value: &'a mut T,
231) -> BorrowMutGuard<'a, T> {
232    let tracked = crate::track_borrow_mut(name, value);
233    BorrowMutGuard::new(name, tracked)
234}
235
236#[cfg(test)]
237mod tests {
238    use super::*;
239    use crate::{get_events, reset};
240    use serial_test::serial;
241
242    #[test]
243    #[serial]
244    fn test_track_guard_auto_drop() {
245        reset();
246        {
247            let _x = track_new_guard("x", 42);
248        }
249        let events = get_events();
250        assert_eq!(events.len(), 2);
251        assert!(events[0].is_new());
252        assert!(events[1].is_drop());
253    }
254
255    #[test]
256    #[serial]
257    fn test_track_guard_deref() {
258        reset();
259        let x = track_new_guard("x", vec![1, 2, 3]);
260        assert_eq!(x.len(), 3);
261        assert_eq!(x[0], 1);
262    }
263
264    #[test]
265    #[serial]
266    fn test_track_guard_deref_mut() {
267        reset();
268        let mut x = track_new_guard("x", vec![1, 2, 3]);
269        x.push(4);
270        assert_eq!(x.len(), 4);
271    }
272
273    #[test]
274    #[serial]
275    fn test_into_inner_no_drop() {
276        reset();
277        let x = track_new_guard("x", 42);
278        let _val = x.into_inner();
279        let events = get_events();
280        assert_eq!(events.len(), 1); // Only New, no Drop
281    }
282
283    #[test]
284    #[serial]
285    fn test_borrow_guard() {
286        reset();
287        let data = track_new_guard("data", 42);
288        {
289            let _r = track_borrow_guard("r", &*data);
290        }
291        let events = get_events();
292        assert_eq!(events.len(), 3); // New, Borrow, Drop(r)
293    }
294
295    #[test]
296    #[serial]
297    fn test_borrow_mut_guard() {
298        reset();
299        let mut data = track_new_guard("data", vec![1]);
300        {
301            let mut r = track_borrow_mut_guard("r", &mut *data);
302            r.push(2);
303        }
304        assert_eq!(data.len(), 2);
305    }
306}