use std::cell::UnsafeCell;
use std::ops::{Deref, DerefMut};
pub struct ScopeCell<'a, T: Clone> {
original_data: &'a T,
modified_data: UnsafeCell<Option<T>>, }
impl<'a, T: Clone> ScopeCell<'a, T> {
pub fn new(data: &'a T) -> Self {
ScopeCell {
original_data: data,
modified_data: UnsafeCell::new(None),
}
}
pub fn into_inner(self) -> T {
if let Some(modified) = unsafe { (*self.modified_data.get()).take() } {
modified
} else {
self.original_data.clone()
}
}
pub fn revert(&mut self) {
unsafe {
*self.modified_data.get() = None;
}
}
pub fn get(&self) -> &T {
if let Some(ref modified) = unsafe { &*self.modified_data.get() } {
modified
} else {
self.original_data
}
}
pub fn get_mut(&self) -> &mut T {
if unsafe { &*self.modified_data.get() }.is_none() {
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()
}
}
impl<'a, T: Clone> Drop for ScopeCell<'a, T> {
fn drop(&mut self) {
self.revert(); }
}
#[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);
} assert_eq!(data, 10); }
#[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(); assert_eq!(scope.get().len(), 3); scope.get_mut().push(5);
}
assert_eq!(data, vec![1, 2, 3]); }
#[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); inner = scope.into_inner(); }
assert_eq!(inner, vec![1, 2, 3, 4]); assert_eq!(data, vec![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(); scope.get_mut().push(5);
scope.revert(); }
assert_eq!(data, vec![1, 2, 3]); }
#[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"); }
assert_eq!(data, "hello"); }
#[test]
fn test_with_copy_type() {
let data = 10;
{
let mut Scope = ScopeCell::new(&data);
*Scope.get_mut() = 20;
assert_eq!(*Scope.get(), 20); }
assert_eq!(data, 10); }
#[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]); }
assert_eq!(data, vec![1, 2, 3]); }
#[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); borrowed.pop(); }
assert_eq!(data, vec![1, 2, 3]); }
#[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]); assert_eq!(data2, vec![4, 5, 6]); }
#[test]
fn test_needs_drop_after_into_inner() {
let data = vec![1, 2, 3];
{
let scope = ScopeCell::new(&data);
let _inner = scope.into_inner(); }
assert_eq!(data.len(), 3);
}
#[test]
fn test_no_mutation_revert() {
let data = vec![1, 2, 3];
{
let scope = ScopeCell::new(&data);
}
assert_eq!(data, vec![1, 2, 3]); }
#[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]); }
#[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); scope.get_mut().push(5); assert_eq!(scope.get().len(), 4); }
assert_eq!(data, vec![1, 2, 3]); }
#[test]
fn test_borrow_and_mut_borrow() {
let data = vec![1, 2, 3];
{
let mut scope = ScopeCell::new(&data);
let borrowed = scope.get(); assert_eq!(borrowed.len(), 3);
let mut borrowed_mut = scope.get_mut(); borrowed_mut.push(4);
assert_eq!(borrowed_mut.len(), 4);
}
assert_eq!(data, vec![1, 2, 3]); }
#[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]); assert_eq!(data2, vec![4, 5, 6]); }
}