#![doc = include_str!("../README.md")]
extern crate self as defer_rs;
#[cfg(not(doc))]
pub use defer_rs_impl::{defer_scope, defer_scope_init};
#[must_use = "Defer MUST be bound to a variable to function properly; otherwise, it will be dropped immediately, executing the enclosed closure!"]
pub struct Defer<T: FnOnce()>(Option<T>);
impl<T: FnOnce()> Defer<T> {
pub fn new(deferred: T) -> Self {
Self(Some(deferred))
}
}
impl<T: FnOnce()> Drop for Defer<T> {
fn drop(&mut self) {
unsafe { (self.0.take().unwrap_unchecked())() }
}
}
#[must_use = "DeferGroup MUST be bound to a variable to function properly; otherwise, it will be dropped immediately, executing the enclosed closure!"]
pub struct DeferGroup<'a>(Vec<Option<Box<dyn FnOnce() + 'a>>>);
impl<'a> DeferGroup<'a> {
pub fn new() -> Self {
Self(Vec::new())
}
pub fn add(&mut self, f: Box<dyn FnOnce() + 'a>) {
self.0.insert(0, Some(f));
}
pub fn push(&mut self, f: Box<dyn FnOnce() + 'a>) {
self.0.push(Some(f));
}
}
impl<'a> Drop for DeferGroup<'a> {
fn drop(self: &mut DeferGroup<'a>) {
for deferred in &mut self.0 {
unsafe { deferred.take().unwrap_unchecked()() };
}
}
}
#[macro_export]
macro_rules! defer{
($(@$move_kw:ident@)? $body:block$(;)?) => {
let ___deferred_code =$crate::Defer::new($($move_kw)?||
$body
);
};
($func:ident($($arg:expr),* $(,)? )) => {
let ___deferred_code_captured_args = ( $( $arg, )* );
let ___deferred_code =$crate::Defer::new(move|| {
::defer_rs_impl::call_indexed!($func($($arg),*));
});
};
(move $($body:tt)+ ) => {
defer!(@move@ {$($body)*})
};
($($body:tt)+ ) => {
defer!({$($body)*})
};
}
#[cfg(doc)]
#[macro_export]
macro_rules! defer_scope { ($($tt:tt)*) => { ... } }
#[cfg(doc)]
#[macro_export]
macro_rules! defer_scope_init { () => { ... } }
#[cfg(test)]
#[allow(unused)]
mod tests {
use super::{defer, defer_scope, defer_scope_init, Defer, DeferGroup};
use std::cell::{Cell, RefCell};
use std::io::Write;
fn print(to_print: String) {
println!("{to_print}");
}
fn add_to_buffer(to_add: String, buff: &RefCell<Vec<u8>>) {
writeln!(buff.borrow_mut(), "{to_add}");
}
#[test]
fn test_execution_order() {
let buff = RefCell::new(Vec::new());
let val = Cell::new(0);
{
defer_scope_init!();
{
defer_scope!({
let res = b"This will be printed 1st, x is: 1\nThis will be printed 2nd\nThis will be printed 3rd\nThis will be printed 4th\nThis will be printed 5th\nThis will be printed 6th\nThis will be printed 7th\nThis will be printed 8th, x is: 3\nThis will be printed 9th\nThis will be printed 10th, x is: 0\nThis will be printed 11th\nThis will be printed 12th\nThis will be printed 13th/last\n";
assert_eq!(*buff.borrow(), res.to_vec());
});
defer_scope!(
writeln!(buff.borrow_mut(), "This will be printed 13th/last");
);
defer_scope!(
writeln!(buff.borrow_mut(), "This will be printed 12th");
);
{
let val = Cell::new(1);
defer_scope_init!();
defer_scope!(add_to_buffer(
format!("This will be printed 1st, x is: {}", val.get()),
&buff
));
val.set(3);
}
defer! {
writeln!(buff.borrow_mut(), "This will be printed 3rd");
writeln!(buff.borrow_mut(), "This will be printed 4th");
};
defer_scope! {
writeln!(buff.borrow_mut(), "This will be printed 11th");
};
defer! {
writeln!(buff.borrow_mut(), "This will be printed 2nd");
};
}
writeln!(buff.borrow_mut(), "This will be printed 5th");
defer_scope!(add_to_buffer(
format!("This will be printed 10th, x is: {}", val.get()),
&buff
));
defer!({
add_to_buffer(
format!("This will be printed 8th, x is: {}", val.get()),
&buff,
)
});
val.set(3);
defer_scope! {
writeln!(buff.borrow_mut(), "This will be printed 9th");
};
writeln!(buff.borrow_mut(), "This will be printed 6th");
defer! {
writeln!(buff.borrow_mut(), "This will be printed 7th");
};
}
}
#[test]
fn test_defer_macro_execution() {
let val = Cell::new(0);
{
defer!(val.set(1));
assert_eq!(val.get(), 0);
}
assert_eq!(val.get(), 1);
}
#[test]
fn test_defer_struct() {
let val = Cell::new(0);
{
let _deferred = Defer::new(|| val.set(1));
assert_eq!(val.get(), 0);
}
assert_eq!(val.get(), 1);
}
#[test]
fn test_defer_scoped_macro_execution() {
let val = Cell::new(0);
{
defer_scope_init!();
{
defer_scope!(val.set(1));
assert_eq!(val.get(), 0);
}
assert_eq!(val.get(), 0);
}
assert_eq!(val.get(), 1)
}
#[test]
fn test_defer_group() {
let val = Cell::new(0);
{
let mut deferred = DeferGroup::new();
{
deferred.add(Box::new(|| val.set(1)));
assert_eq!(val.get(), 0);
}
assert_eq!(val.get(), 0);
}
assert_eq!(val.get(), 1)
}
#[test]
fn test_defer_macro_immediate_args_eval() {
let buff = RefCell::new(Vec::new());
let buff2 = RefCell::new(Vec::new());
let val = Cell::new(0);
defer! {
let res = b"x is: 3\n";
assert_eq!(*buff.borrow(), res.to_vec());
let res = b"x is: 0\n";
assert_eq!(*buff2.borrow(), res.to_vec());
};
defer!(
add_to_buffer(
format!("x is: {}", val.get()),
&buff
);
);
defer!(add_to_buffer(format!("x is: {}", val.get()), &buff2));
val.set(3);
}
#[test]
fn test_defer_scope_macro_immediate_args_eval() {
let buff = RefCell::new(Vec::new());
let buff2 = RefCell::new(Vec::new());
let val = Cell::new(0);
defer_scope_init!();
defer_scope! {
let res = b"x is: 3\n";
assert_eq!(*buff.borrow(), res.to_vec());
let res = b"x is: 0\n";
assert_eq!(*buff2.borrow(), res.to_vec());
};
defer_scope!(
add_to_buffer(
format!("x is: {}", val.get()),
&buff
);
);
defer_scope!(add_to_buffer(format!("x is: {}", val.get()), &buff2));
val.set(3);
}
}