use core::{
marker::PhantomData,
mem::ManuallyDrop,
ops::{Deref, DerefMut},
};
pub trait RevertCheck<D: ?Sized> {
type Snapshot;
fn ensure_same(from: &D, other: Self::Snapshot);
fn snapshot(from: &D) -> Self::Snapshot;
}
pub struct CloneEqPanicRevertCheck;
impl<D: Eq + Clone> RevertCheck<D> for CloneEqPanicRevertCheck {
type Snapshot = D;
fn ensure_same(from: &D, other: Self::Snapshot) {
#[cfg(feature = "std")]
if std::thread::panicking() {
return;
}
if from != &other {
panic!("Value was modified and not returned to its original state");
}
}
fn snapshot(from: &D) -> Self::Snapshot {
from.clone()
}
}
pub struct ControlledRevertCheck<C>(PhantomData<C>);
impl<D, C: RevertCheck<D>> RevertCheck<D> for ControlledRevertCheck<C> {
type Snapshot = cfg_select! {
debug_assertions => C::Snapshot,
_ => (),
};
#[cfg_attr(not(debug_assertions), allow(unused_variables))]
fn ensure_same(from: &D, other: Self::Snapshot) {
#[cfg(debug_assertions)]
C::ensure_same(from, other);
}
#[cfg_attr(not(debug_assertions), allow(unused_variables))]
fn snapshot(from: &D) -> Self::Snapshot {
cfg_select! {
debug_assertions => C::snapshot(from),
_ => (),
}
}
}
type DefaultRevertCheck = ControlledRevertCheck<CloneEqPanicRevertCheck>;
pub struct RevMut<D: DerefMut, C: RevertCheck<D::Target> = DefaultRevertCheck> {
new: ManuallyDrop<D>,
old: ManuallyDrop<C::Snapshot>,
}
impl<D: DerefMut, C: RevertCheck<D::Target>> RevMut<D, C> {
pub fn new(value: D) -> Self {
Self {
old: ManuallyDrop::new(C::snapshot(value.deref())),
new: ManuallyDrop::new(value),
}
}
unsafe fn unwrap_core(this: &mut Self) -> D {
let old = unsafe { ManuallyDrop::take(&mut this.old) };
let new = unsafe { ManuallyDrop::take(&mut this.new) };
C::ensure_same(&new, old);
new
}
pub fn unwrap(this: Self) -> D {
let mut this = ManuallyDrop::new(this);
let new = unsafe { Self::unwrap_core(&mut this) };
new
}
}
impl<D: DerefMut, C: RevertCheck<D::Target>> Drop for RevMut<D, C> {
fn drop(&mut self) {
_ = unsafe { Self::unwrap_core(self) };
}
}
impl<D: DerefMut, C: RevertCheck<D::Target>> Deref for RevMut<D, C> {
type Target = D::Target;
fn deref(&self) -> &Self::Target {
self.new.deref()
}
}
impl<D: DerefMut, C: RevertCheck<D::Target>> DerefMut for RevMut<D, C> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.new.deref_mut()
}
}