scope_cell/
lib.rs

1use std::cell::UnsafeCell;
2//use std::mem::ManuallyDrop;
3use std::ops::{Deref, DerefMut};
4
5/// A ScopeCell allows temporary, scope-bound mutations to a value.  The underlying
6/// data must implement `Copy` so that the original value can be efficiently stored
7/// and restored.  Changes made within the ScopeCell's scope are reverted when the
8/// ScopeCell is dropped.
9pub struct ScopeCell<'a, T: Clone> {
10    original_data: &'a T,
11    modified_data: UnsafeCell<Option<T>>, // Holds temporary modified data
12}
13
14impl<'a, T: Clone> ScopeCell<'a, T> {
15    // Create a new ScopeCell from an immutable reference
16    pub fn new(data: &'a T) -> Self {
17        ScopeCell {
18            original_data: data,
19            modified_data: UnsafeCell::new(None),
20        }
21    }
22
23    // Consume the ScopeCell and return the inner modified data if it exists, otherwise return the original data
24    pub fn into_inner(self) -> T {
25        if let Some(modified) = unsafe { (*self.modified_data.get()).take() } {
26            modified
27        } else {
28            self.original_data.clone()
29        }
30    }
31
32    // Revert the changes made to the data by dropping the modified data
33    pub fn revert(&mut self) {
34        unsafe {
35            *self.modified_data.get() = None;
36        }
37    }
38
39    // Borrow the data, showing either the original or the modified version
40    pub fn get(&self) -> &T {
41        if let Some(ref modified) = unsafe { &*self.modified_data.get() } {
42            modified
43        } else {
44            self.original_data
45        }
46    }
47
48    // Mutably borrow the data, creating a temporary mutable copy if necessary
49    pub fn get_mut(&self) -> &mut T {
50        if unsafe { &*self.modified_data.get() }.is_none() {
51            // If no modification exists, clone the original data
52            unsafe {
53                *self.modified_data.get() = Some(self.original_data.clone());
54            }
55        }
56
57        unsafe { (*self.modified_data.get()).as_mut().unwrap() }
58    }
59}
60
61pub struct ScopeBorrow<'b, T: Clone> {
62    cell: &'b ScopeCell<'b, T>,
63}
64
65impl<'b, T: Clone> Deref for ScopeBorrow<'b, T> {
66    type Target = T;
67
68    fn deref(&self) -> &Self::Target {
69        self.cell.get()
70    }
71}
72
73pub struct ScopeBorrowMut<'b, T: Clone> {
74    cell: &'b mut ScopeCell<'b, T>,
75}
76
77impl<'b, T: Clone> Deref for ScopeBorrowMut<'b, T> {
78    type Target = T;
79
80    fn deref(&self) -> &Self::Target {
81        self.cell.get()
82    }
83}
84
85impl<'b, T: Clone> DerefMut for ScopeBorrowMut<'b, T> {
86    fn deref_mut(&mut self) -> &mut Self::Target {
87        self.cell.get_mut()
88    }
89}
90
91// When the ScopeCell is dropped, changes are discarded automatically.
92impl<'a, T: Clone> Drop for ScopeCell<'a, T> {
93    fn drop(&mut self) {
94        self.revert(); // Drop the modified data, reverting any changes.
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn test_basic_revert() {
104        let data = 10;
105        {
106            let mut scope = ScopeCell::new(&data);
107            *scope.get_mut() = 20;
108            assert_eq!(*scope.get(), 20);
109        } // ScopeCell is dropped here, and data should revert
110        assert_eq!(data, 10); // Original value should be restored
111    }
112
113    #[test]
114    fn test_revert_mid_scope() {
115        let data = vec![1, 2, 3];
116        {
117            let mut scope = ScopeCell::new(&data);
118            scope.get_mut().push(4);
119            scope.revert(); // Revert midway through mutation
120            assert_eq!(scope.get().len(), 3); // Should revert to original length
121            scope.get_mut().push(5);
122        }
123        assert_eq!(data, vec![1, 2, 3]); // Original data should not change
124    }
125
126    #[test]
127    fn test_into_inner_no_revert() {
128        let data = vec![1, 2, 3];
129        let inner;
130        {
131            let mut scope = ScopeCell::new(&data);
132            scope.get_mut().push(4); // Modify the data inside the ScopeCell
133            inner = scope.into_inner(); // Take ownership of the modified data
134        }
135        assert_eq!(inner, vec![1, 2, 3, 4]); // The modified value should be `[1, 2, 3, 4]`
136        assert_eq!(data, vec![1, 2, 3]); // Ensure the original data remains `[1, 2, 3]`
137    }
138
139    #[test]
140    fn test_multiple_reverts() {
141        let data = vec![1, 2, 3];
142        {
143            let mut scope = ScopeCell::new(&data);
144            scope.get_mut().push(4);
145            scope.revert(); // Revert first mutation
146            scope.get_mut().push(5);
147            scope.revert(); // Revert second mutation
148        }
149        assert_eq!(data, vec![1, 2, 3]); // Original data should be restored
150    }
151
152    #[test]
153    fn test_with_string_mutation() {
154        let data = String::from("hello");
155        {
156            let mut scope = ScopeCell::new(&data);
157            scope.get_mut().push_str(" world");
158            assert_eq!(*scope.get(), "hello world"); // Check the modified string
159        }
160        assert_eq!(data, "hello"); // Original string should remain unchanged
161    }
162
163    #[test]
164    fn test_with_copy_type() {
165        let data = 10;
166        {
167            let mut Scope = ScopeCell::new(&data);
168            *Scope.get_mut() = 20;
169            assert_eq!(*Scope.get(), 20); // Mutated value
170        }
171        assert_eq!(data, 10); // Reverted to original value
172    }
173
174    #[test]
175    fn test_with_needs_drop_type() {
176        let data = vec![1, 2, 3];
177        {
178            let mut Scope = ScopeCell::new(&data);
179            Scope.get_mut().push(4);
180            assert_eq!(*Scope.get(), vec![1, 2, 3, 4]); // Mutated vector
181        }
182        assert_eq!(data, vec![1, 2, 3]); // Reverted to original vector
183    }
184
185    #[test]
186    fn test_nested_borrows() {
187        let data = vec![1, 2, 3];
188        {
189            let mut Scope = ScopeCell::new(&data);
190            let mut borrowed = Scope.get_mut();
191            borrowed.push(4);
192            assert_eq!(borrowed.len(), 4); // Ensure the borrow mutates
193            borrowed.pop(); // Modify through the mutable borrow
194        }
195        assert_eq!(data, vec![1, 2, 3]); // Reverted to original vector
196    }
197
198    #[test]
199    fn test_multiple_scope_cells() {
200        let data1 = vec![1, 2, 3];
201        let data2 = vec![4, 5, 6];
202
203        {
204            let mut scope1 = ScopeCell::new(&data1);
205            let mut scope2 = ScopeCell::new(&data2);
206
207            scope1.get_mut().push(4);
208            scope2.get_mut().push(7);
209        }
210        assert_eq!(data1, vec![1, 2, 3]); // Must revert
211        assert_eq!(data2, vec![4, 5, 6]); // Must revert
212    }
213
214    #[test]
215    fn test_needs_drop_after_into_inner() {
216        let data = vec![1, 2, 3];
217        {
218            let scope = ScopeCell::new(&data);
219            let _inner = scope.into_inner(); // Take ownership
220        }
221        // Ensure original data is unaffected after the ScopeCell is dropped
222        assert_eq!(data.len(), 3);
223    }
224
225    #[test]
226    fn test_no_mutation_revert() {
227        let data = vec![1, 2, 3];
228        {
229            let scope = ScopeCell::new(&data);
230            // No mutation performed
231        }
232        assert_eq!(data, vec![1, 2, 3]); // Original data should remain unchanged
233    }
234
235    #[test]
236    fn test_multiple_borrow_same_scope() {
237        let data = vec![1, 2, 3];
238        {
239            let mut scope = ScopeCell::new(&data);
240            let borrowed1 = scope.get();
241            let borrowed2 = scope.get();
242            assert_eq!(borrowed1.len(), 3);
243            assert_eq!(borrowed2.len(), 3);
244        }
245        assert_eq!(data, vec![1, 2, 3]); // No mutation, data should remain unchanged
246    }
247
248    #[test]
249    fn test_mutation_after_revert() {
250        let data = vec![1, 2, 3];
251        {
252            let mut scope = ScopeCell::new(&data);
253            scope.get_mut().push(4);
254            scope.revert();
255            assert_eq!(scope.get().len(), 3); // Reverted to original length
256            scope.get_mut().push(5); // New mutation after revert
257            assert_eq!(scope.get().len(), 4); // Should reflect the new mutation
258        }
259        assert_eq!(data, vec![1, 2, 3]); // Original data should not change
260    }
261
262    #[test]
263    fn test_borrow_and_mut_borrow() {
264        let data = vec![1, 2, 3];
265        {
266            let mut scope = ScopeCell::new(&data);
267            let borrowed = scope.get(); // Immutable borrow
268            assert_eq!(borrowed.len(), 3);
269
270            let mut borrowed_mut = scope.get_mut(); // Mutable borrow
271            borrowed_mut.push(4);
272            assert_eq!(borrowed_mut.len(), 4);
273        }
274        assert_eq!(data, vec![1, 2, 3]); // Original data should remain unchanged
275    }
276
277    #[test]
278    fn test_nested_scope_cell() {
279        let data1 = vec![1, 2, 3];
280        let data2 = vec![4, 5, 6];
281        {
282            let mut outer_scope = ScopeCell::new(&data1);
283            let mut inner_scope = ScopeCell::new(&data2);
284            inner_scope.get_mut().push(7);
285            outer_scope.get_mut().push(4);
286
287            assert_eq!(inner_scope.get(), &vec![4, 5, 6, 7]);
288            assert_eq!(outer_scope.get(), &vec![1, 2, 3, 4]);
289        }
290        assert_eq!(data1, vec![1, 2, 3]); // Must revert
291        assert_eq!(data2, vec![4, 5, 6]); // Must revert
292    }
293}