async-safe-defer 0.2.0

Runtime-independent LIFO cleanup scopes for synchronous and asynchronous Rust
Documentation
#![no_std]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]

#[cfg(feature = "alloc")]
extern crate alloc;

#[cfg(feature = "std")]
extern crate std;

mod sync;

#[cfg(feature = "alloc")]
mod async_scope;

pub mod fixed;

pub use sync::{defer, guard, Always, DeferGuard, ScopeGuard, Strategy};

#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub use sync::{
    defer_on_success, defer_on_unwind, guard_on_success, guard_on_unwind, OnSuccess, OnUnwind,
};

#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub use async_scope::{AsyncScope, LocalAsyncScope, LocalRun, SendAsyncScope, SendRun};

/// Binds an armed [`DeferGuard`] to the surrounding lexical scope.
///
/// Use `defer!(move { ... })` when the action should own captured values.
/// The body must evaluate to `()`, so fallible cleanup must handle its result.
///
/// ```compile_fail
/// use async_safe_defer::defer;
///
/// fn fallible_cleanup() -> Result<(), ()> {
///     Ok(())
/// }
///
/// defer!(fallible_cleanup());
/// ```
#[macro_export]
macro_rules! defer {
    (move $($body:tt)*) => {
        let _defer_guard = $crate::defer(move || {
            $($body)*
        });
    };
    ($($body:tt)*) => {
        let _defer_guard = $crate::defer(|| {
            $($body)*
        });
    };
}

/// Runs a body with a local asynchronous cleanup scope.
///
/// After the body completes, registered actions run before its value is
/// returned. A `return` or `?` exits the body future, so cleanup still runs.
/// Panicking or dropping the returned future may leave cleanup actions unrun.
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
#[macro_export]
macro_rules! async_scope {
    ($scope:ident, $body:block) => {
        async {
            let mut $scope = $crate::LocalAsyncScope::new();
            let __async_safe_defer_result = (async $body).await;
            $scope.finish().await;
            __async_safe_defer_result
        }
    };
}

/// Runs a body with a `Send` asynchronous cleanup scope.
///
/// Registered cleanup closures and their futures must be `Send`; whether the
/// returned future is `Send` also depends on the body. Its exit and drop
/// behavior matches [`async_scope!`].
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
#[macro_export]
macro_rules! send_async_scope {
    ($scope:ident, $body:block) => {
        async {
            let mut $scope = $crate::SendAsyncScope::new();
            let __async_safe_defer_result = (async $body).await;
            $scope.finish().await;
            __async_safe_defer_result
        }
    };
}