#[must_use]
pub struct DeferGuard<F: FnOnce()>(pub Option<F>);
impl<F: FnOnce()> Drop for DeferGuard<F> {
fn drop(&mut self) {
self.0.take().map(|f| f());
}
}
#[macro_export]
macro_rules! defer(
( $( $code:tt )* ) => {
let _guard = $crate::defer::DeferGuard(Some(|| { $( $code )* }));
};
);
#[cfg(test)]
mod test {
#[test]
fn defer_guard_works() {
let mut called = false;
{
defer!(
called = true;
);
}
assert!(called, "DeferGuard should have executed the closure");
}
#[test]
fn defer_guard_order_works() {
let called = std::cell::RefCell::new(1);
defer!(
assert_eq!(*called.borrow(), 3);
);
defer!(
assert_eq!(*called.borrow(), 2);
*called.borrow_mut() = 3;
);
defer!({
assert_eq!(*called.borrow(), 1);
*called.borrow_mut() = 2;
});
}
#[test]
#[allow(unused_braces)]
#[allow(clippy::unnecessary_operation)]
fn defer_guard_syntax_works() {
let called = std::cell::RefCell::new(0);
{
defer!(*called.borrow_mut() += 1);
defer!(*called.borrow_mut() += 1;); defer!({ *called.borrow_mut() += 1 });
defer!({ *called.borrow_mut() += 1 };); }
assert_eq!(*called.borrow(), 4);
}
#[test]
fn defer_guard_panic_unwind_works() {
use std::panic::{catch_unwind, AssertUnwindSafe};
let mut called = false;
let should_panic = catch_unwind(AssertUnwindSafe(|| {
defer!(called = true);
panic!();
}));
assert!(should_panic.is_err(), "DeferGuard should have panicked");
assert!(called, "DeferGuard should have executed the closure");
}
#[test]
fn defer_guard_defer_panics_unwind_works() {
use std::panic::{catch_unwind, AssertUnwindSafe};
let counter = std::cell::RefCell::new(0);
let should_panic = catch_unwind(AssertUnwindSafe(|| {
defer!(*counter.borrow_mut() += 1);
defer!(
*counter.borrow_mut() += 1;
panic!();
);
defer!(*counter.borrow_mut() += 1);
}));
assert!(should_panic.is_err(), "DeferGuard should have panicked");
assert_eq!(*counter.borrow(), 3, "DeferGuard should have executed the closure");
}
}