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
241    #[test]
242    fn test_track_guard_auto_drop() {
243        reset();
244        {
245            let _x = track_new_guard("x", 42);
246        }
247        let events = get_events();
248        assert_eq!(events.len(), 2);
249        assert!(events[0].is_new());
250        assert!(events[1].is_drop());
251    }
252
253    #[test]
254    fn test_track_guard_deref() {
255        reset();
256        let x = track_new_guard("x", vec![1, 2, 3]);
257        assert_eq!(x.len(), 3);
258        assert_eq!(x[0], 1);
259    }
260
261    #[test]
262    fn test_track_guard_deref_mut() {
263        reset();
264        let mut x = track_new_guard("x", vec![1, 2, 3]);
265        x.push(4);
266        assert_eq!(x.len(), 4);
267    }
268
269    #[test]
270    fn test_into_inner_no_drop() {
271        reset();
272        let x = track_new_guard("x", 42);
273        let _val = x.into_inner();
274        let events = get_events();
275        assert_eq!(events.len(), 1); // Only New, no Drop
276    }
277
278    #[test]
279    fn test_borrow_guard() {
280        reset();
281        let data = track_new_guard("data", 42);
282        {
283            let _r = track_borrow_guard("r", &*data);
284        }
285        let events = get_events();
286        assert_eq!(events.len(), 3); // New, Borrow, Drop(r)
287    }
288
289    #[test]
290    fn test_borrow_mut_guard() {
291        reset();
292        let mut data = track_new_guard("data", vec![1]);
293        {
294            let mut r = track_borrow_mut_guard("r", &mut *data);
295            r.push(2);
296        }
297        assert_eq!(data.len(), 2);
298    }
299}