scope_cell/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
use std::cell::UnsafeCell;
//use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut};

/// A ScopeCell allows temporary, scope-bound mutations to a value.  The underlying
/// data must implement `Copy` so that the original value can be efficiently stored
/// and restored.  Changes made within the ScopeCell's scope are reverted when the
/// ScopeCell is dropped.
pub struct ScopeCell<'a, T: Clone> {
    original_data: &'a T,
    modified_data: UnsafeCell<Option<T>>, // Holds temporary modified data
}

impl<'a, T: Clone> ScopeCell<'a, T> {
    // Create a new ScopeCell from an immutable reference
    pub fn new(data: &'a T) -> Self {
        ScopeCell {
            original_data: data,
            modified_data: UnsafeCell::new(None),
        }
    }

    // Consume the ScopeCell and return the inner modified data if it exists, otherwise return the original data
    pub fn into_inner(self) -> T {
        if let Some(modified) = unsafe { (*self.modified_data.get()).take() } {
            modified
        } else {
            self.original_data.clone()
        }
    }

    // Revert the changes made to the data by dropping the modified data
    pub fn revert(&mut self) {
        unsafe {
            *self.modified_data.get() = None;
        }
    }

    // Borrow the data, showing either the original or the modified version
    pub fn get(&self) -> &T {
        if let Some(ref modified) = unsafe { &*self.modified_data.get() } {
            modified
        } else {
            self.original_data
        }
    }

    // Mutably borrow the data, creating a temporary mutable copy if necessary
    pub fn get_mut(&self) -> &mut T {
        if unsafe { &*self.modified_data.get() }.is_none() {
            // If no modification exists, clone the original data
            unsafe {
                *self.modified_data.get() = Some(self.original_data.clone());
            }
        }

        unsafe { (*self.modified_data.get()).as_mut().unwrap() }
    }
}

pub struct ScopeBorrow<'b, T: Clone> {
    cell: &'b ScopeCell<'b, T>,
}

impl<'b, T: Clone> Deref for ScopeBorrow<'b, T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        self.cell.get()
    }
}

pub struct ScopeBorrowMut<'b, T: Clone> {
    cell: &'b mut ScopeCell<'b, T>,
}

impl<'b, T: Clone> Deref for ScopeBorrowMut<'b, T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        self.cell.get()
    }
}

impl<'b, T: Clone> DerefMut for ScopeBorrowMut<'b, T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.cell.get_mut()
    }
}

// When the ScopeCell is dropped, changes are discarded automatically.
impl<'a, T: Clone> Drop for ScopeCell<'a, T> {
    fn drop(&mut self) {
        self.revert(); // Drop the modified data, reverting any changes.
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_basic_revert() {
        let data = 10;
        {
            let mut scope = ScopeCell::new(&data);
            *scope.get_mut() = 20;
            assert_eq!(*scope.get(), 20);
        } // ScopeCell is dropped here, and data should revert
        assert_eq!(data, 10); // Original value should be restored
    }

    #[test]
    fn test_revert_mid_scope() {
        let data = vec![1, 2, 3];
        {
            let mut scope = ScopeCell::new(&data);
            scope.get_mut().push(4);
            scope.revert(); // Revert midway through mutation
            assert_eq!(scope.get().len(), 3); // Should revert to original length
            scope.get_mut().push(5);
        }
        assert_eq!(data, vec![1, 2, 3]); // Original data should not change
    }

    #[test]
    fn test_into_inner_no_revert() {
        let data = vec![1, 2, 3];
        let inner;
        {
            let mut scope = ScopeCell::new(&data);
            scope.get_mut().push(4); // Modify the data inside the ScopeCell
            inner = scope.into_inner(); // Take ownership of the modified data
        }
        assert_eq!(inner, vec![1, 2, 3, 4]); // The modified value should be `[1, 2, 3, 4]`
        assert_eq!(data, vec![1, 2, 3]); // Ensure the original data remains `[1, 2, 3]`
    }

    #[test]
    fn test_multiple_reverts() {
        let data = vec![1, 2, 3];
        {
            let mut scope = ScopeCell::new(&data);
            scope.get_mut().push(4);
            scope.revert(); // Revert first mutation
            scope.get_mut().push(5);
            scope.revert(); // Revert second mutation
        }
        assert_eq!(data, vec![1, 2, 3]); // Original data should be restored
    }

    #[test]
    fn test_with_string_mutation() {
        let data = String::from("hello");
        {
            let mut scope = ScopeCell::new(&data);
            scope.get_mut().push_str(" world");
            assert_eq!(*scope.get(), "hello world"); // Check the modified string
        }
        assert_eq!(data, "hello"); // Original string should remain unchanged
    }

    #[test]
    fn test_with_copy_type() {
        let data = 10;
        {
            let mut Scope = ScopeCell::new(&data);
            *Scope.get_mut() = 20;
            assert_eq!(*Scope.get(), 20); // Mutated value
        }
        assert_eq!(data, 10); // Reverted to original value
    }

    #[test]
    fn test_with_needs_drop_type() {
        let data = vec![1, 2, 3];
        {
            let mut Scope = ScopeCell::new(&data);
            Scope.get_mut().push(4);
            assert_eq!(*Scope.get(), vec![1, 2, 3, 4]); // Mutated vector
        }
        assert_eq!(data, vec![1, 2, 3]); // Reverted to original vector
    }

    #[test]
    fn test_nested_borrows() {
        let data = vec![1, 2, 3];
        {
            let mut Scope = ScopeCell::new(&data);
            let mut borrowed = Scope.get_mut();
            borrowed.push(4);
            assert_eq!(borrowed.len(), 4); // Ensure the borrow mutates
            borrowed.pop(); // Modify through the mutable borrow
        }
        assert_eq!(data, vec![1, 2, 3]); // Reverted to original vector
    }

    #[test]
    fn test_multiple_scope_cells() {
        let data1 = vec![1, 2, 3];
        let data2 = vec![4, 5, 6];

        {
            let mut scope1 = ScopeCell::new(&data1);
            let mut scope2 = ScopeCell::new(&data2);

            scope1.get_mut().push(4);
            scope2.get_mut().push(7);
        }
        assert_eq!(data1, vec![1, 2, 3]); // Must revert
        assert_eq!(data2, vec![4, 5, 6]); // Must revert
    }

    #[test]
    fn test_needs_drop_after_into_inner() {
        let data = vec![1, 2, 3];
        {
            let scope = ScopeCell::new(&data);
            let _inner = scope.into_inner(); // Take ownership
        }
        // Ensure original data is unaffected after the ScopeCell is dropped
        assert_eq!(data.len(), 3);
    }

    #[test]
    fn test_no_mutation_revert() {
        let data = vec![1, 2, 3];
        {
            let scope = ScopeCell::new(&data);
            // No mutation performed
        }
        assert_eq!(data, vec![1, 2, 3]); // Original data should remain unchanged
    }

    #[test]
    fn test_multiple_borrow_same_scope() {
        let data = vec![1, 2, 3];
        {
            let mut scope = ScopeCell::new(&data);
            let borrowed1 = scope.get();
            let borrowed2 = scope.get();
            assert_eq!(borrowed1.len(), 3);
            assert_eq!(borrowed2.len(), 3);
        }
        assert_eq!(data, vec![1, 2, 3]); // No mutation, data should remain unchanged
    }

    #[test]
    fn test_mutation_after_revert() {
        let data = vec![1, 2, 3];
        {
            let mut scope = ScopeCell::new(&data);
            scope.get_mut().push(4);
            scope.revert();
            assert_eq!(scope.get().len(), 3); // Reverted to original length
            scope.get_mut().push(5); // New mutation after revert
            assert_eq!(scope.get().len(), 4); // Should reflect the new mutation
        }
        assert_eq!(data, vec![1, 2, 3]); // Original data should not change
    }

    #[test]
    fn test_borrow_and_mut_borrow() {
        let data = vec![1, 2, 3];
        {
            let mut scope = ScopeCell::new(&data);
            let borrowed = scope.get(); // Immutable borrow
            assert_eq!(borrowed.len(), 3);

            let mut borrowed_mut = scope.get_mut(); // Mutable borrow
            borrowed_mut.push(4);
            assert_eq!(borrowed_mut.len(), 4);
        }
        assert_eq!(data, vec![1, 2, 3]); // Original data should remain unchanged
    }

    #[test]
    fn test_nested_scope_cell() {
        let data1 = vec![1, 2, 3];
        let data2 = vec![4, 5, 6];
        {
            let mut outer_scope = ScopeCell::new(&data1);
            let mut inner_scope = ScopeCell::new(&data2);
            inner_scope.get_mut().push(7);
            outer_scope.get_mut().push(4);

            assert_eq!(inner_scope.get(), &vec![4, 5, 6, 7]);
            assert_eq!(outer_scope.get(), &vec![1, 2, 3, 4]);
        }
        assert_eq!(data1, vec![1, 2, 3]); // Must revert
        assert_eq!(data2, vec![4, 5, 6]); // Must revert
    }
}