extern crate core as std;
use std::fmt;
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut};
use std::ptr;
pub trait Strategy {
fn should_run() -> bool;
}
#[derive(Debug)]
pub enum Always {}
impl Strategy for Always {
#[inline(always)]
fn should_run() -> bool {
true
}
}
#[macro_export]
macro_rules! defer {
($($t:tt)*) => {
let _guard = $crate::external::scopeguard::guard((), |()| { $($t)* });
};
}
pub(crate) use defer;
pub struct ScopeGuard<T, F, S = Always>
where
F: FnOnce(T),
S: Strategy,
{
value: ManuallyDrop<T>,
dropfn: ManuallyDrop<F>,
strategy: PhantomData<fn(S) -> S>,
}
impl<T, F, S> ScopeGuard<T, F, S>
where
F: FnOnce(T),
S: Strategy,
{
#[inline]
#[must_use]
pub fn with_strategy(v: T, dropfn: F) -> ScopeGuard<T, F, S> {
ScopeGuard {
value: ManuallyDrop::new(v),
dropfn: ManuallyDrop::new(dropfn),
strategy: PhantomData,
}
}
#[inline]
#[allow(unused)]
pub fn into_inner(guard: Self) -> T {
let mut guard = ManuallyDrop::new(guard);
unsafe {
let value = ptr::read(&*guard.value);
ManuallyDrop::drop(&mut guard.dropfn);
value
}
}
}
#[inline]
#[must_use]
pub fn guard<T, F>(v: T, dropfn: F) -> ScopeGuard<T, F, Always>
where
F: FnOnce(T),
{
ScopeGuard::with_strategy(v, dropfn)
}
unsafe impl<T, F, S> Sync for ScopeGuard<T, F, S>
where
T: Sync,
F: FnOnce(T),
S: Strategy,
{
}
impl<T, F, S> Deref for ScopeGuard<T, F, S>
where
F: FnOnce(T),
S: Strategy,
{
type Target = T;
fn deref(&self) -> &T {
&*self.value
}
}
impl<T, F, S> DerefMut for ScopeGuard<T, F, S>
where
F: FnOnce(T),
S: Strategy,
{
fn deref_mut(&mut self) -> &mut T {
&mut *self.value
}
}
impl<T, F, S> Drop for ScopeGuard<T, F, S>
where
F: FnOnce(T),
S: Strategy,
{
fn drop(&mut self) {
let (value, dropfn) = unsafe { (ptr::read(&*self.value), ptr::read(&*self.dropfn)) };
if S::should_run() {
dropfn(value);
}
}
}
impl<T, F, S> fmt::Debug for ScopeGuard<T, F, S>
where
T: fmt::Debug,
F: FnOnce(T),
S: Strategy,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct(stringify!(ScopeGuard))
.field("value", &*self.value)
.finish()
}
}