Skip to main content

async_safe_defer/
lib.rs

1#![no_std]
2#![forbid(unsafe_code)]
3#![warn(missing_docs)]
4#![cfg_attr(docsrs, feature(doc_cfg))]
5#![doc = include_str!("../README.md")]
6
7#[cfg(feature = "alloc")]
8extern crate alloc;
9
10#[cfg(feature = "std")]
11extern crate std;
12
13mod sync;
14
15#[cfg(feature = "alloc")]
16mod async_scope;
17
18pub mod fixed;
19
20pub use sync::{defer, guard, Always, DeferGuard, ScopeGuard, Strategy};
21
22#[cfg(feature = "std")]
23#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
24pub use sync::{
25    defer_on_success, defer_on_unwind, guard_on_success, guard_on_unwind, OnSuccess, OnUnwind,
26};
27
28#[cfg(feature = "alloc")]
29#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
30pub use async_scope::{AsyncScope, LocalAsyncScope, LocalRun, SendAsyncScope, SendRun};
31
32/// Binds an armed [`DeferGuard`] to the surrounding lexical scope.
33///
34/// Use `defer!(move { ... })` when the action should own captured values.
35/// The body must evaluate to `()`, so fallible cleanup must handle its result.
36///
37/// ```compile_fail
38/// use async_safe_defer::defer;
39///
40/// fn fallible_cleanup() -> Result<(), ()> {
41///     Ok(())
42/// }
43///
44/// defer!(fallible_cleanup());
45/// ```
46#[macro_export]
47macro_rules! defer {
48    (move $($body:tt)*) => {
49        let _defer_guard = $crate::defer(move || {
50            $($body)*
51        });
52    };
53    ($($body:tt)*) => {
54        let _defer_guard = $crate::defer(|| {
55            $($body)*
56        });
57    };
58}
59
60/// Runs a body with a local asynchronous cleanup scope.
61///
62/// After the body completes, registered actions run before its value is
63/// returned. A `return` or `?` exits the body future, so cleanup still runs.
64/// Panicking or dropping the returned future may leave cleanup actions unrun.
65#[cfg(feature = "alloc")]
66#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
67#[macro_export]
68macro_rules! async_scope {
69    ($scope:ident, $body:block) => {
70        async {
71            let mut $scope = $crate::LocalAsyncScope::new();
72            let __async_safe_defer_result = (async $body).await;
73            $scope.finish().await;
74            __async_safe_defer_result
75        }
76    };
77}
78
79/// Runs a body with a `Send` asynchronous cleanup scope.
80///
81/// Registered cleanup closures and their futures must be `Send`; whether the
82/// returned future is `Send` also depends on the body. Its exit and drop
83/// behavior matches [`async_scope!`].
84#[cfg(feature = "alloc")]
85#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
86#[macro_export]
87macro_rules! send_async_scope {
88    ($scope:ident, $body:block) => {
89        async {
90            let mut $scope = $crate::SendAsyncScope::new();
91            let __async_safe_defer_result = (async $body).await;
92            $scope.finish().await;
93            __async_safe_defer_result
94        }
95    };
96}