scope_guard/
async_scope.rs

1extern crate std;
2
3use std::boxed::Box;
4use std::panic;
5
6use core::future::Future;
7use core::any::Any;
8use core::pin::Pin;
9use core::task;
10
11#[must_use]
12///Wraps to propagate panic as error.
13///
14///## Example
15///
16///```rust
17///use scope_guard::CatchUnwindFut;
18///
19///async fn my_fut() -> Result<(), bool> {
20///    Err(true)
21///}
22///
23///async fn example() {
24///    match CatchUnwindFut(my_fut()).await {
25///        Ok(Ok(())) => panic!("Success!?"),
26///        Ok(Err(res)) => assert!(res),
27///        Err(panic) => std::panic::resume_unwind(panic),
28///    }
29///}
30///```
31pub struct CatchUnwindFut<F: panic::UnwindSafe>(pub F);
32
33impl<F: Future + panic::UnwindSafe> Future for CatchUnwindFut<F> {
34    type Output = Result<F::Output, Box<dyn Any + Send + 'static>>;
35
36    #[inline(always)]
37    fn poll(self: Pin<&mut Self>, ctx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
38        let fut = unsafe {
39            self.map_unchecked_mut(|this| &mut this.0)
40        };
41
42        match panic::catch_unwind(panic::AssertUnwindSafe(|| fut.poll(ctx))) {
43            Ok(task::Poll::Pending) => task::Poll::Pending,
44            Ok(task::Poll::Ready(res)) => task::Poll::Ready(Ok(res)),
45            Err(error) => task::Poll::Ready(Err(error)),
46        }
47    }
48}
49
50///Executes future, making sure to perform cleanup regardless of whether `fut` is successful or
51///panics.
52///
53///## Arguments:
54///- `dtor` - Generic callback that accepts `args` as its only incoming parameter;
55///- `args` - Generic arguments that are passed as it is to the `dtor`;
56///- `fut` - Future to execute before calling `dtor`. Regardless of success, `dtor` is always
57///executed.
58///
59///Returns `Output` of `fut` or panics on error in executing `fut`.
60///Regardless of `fut` execution status, `dtor` is always called.
61///
62///## Example
63///
64///```rust
65///use scope_guard::async_scope;
66///
67///async fn dtor(_args: ()) {
68///    println!("dtor!");
69///}
70///
71///async fn example() {
72///    let fut = async {
73///        panic!("FAIL")
74///    };
75///
76///    async_scope(dtor, (), fut).await;
77///}
78///```
79pub async fn async_scope<
80    R,
81    F: Future<Output = R> + panic::UnwindSafe,
82    DTORARGS,
83    DTOR: Future<Output = ()>,
84    DTORFN: FnOnce(DTORARGS) -> DTOR,
85>(
86    dtor: DTORFN,
87    args: DTORARGS,
88    fut: F,
89) -> R {
90    let result = CatchUnwindFut(fut).await;
91    let dtor = (dtor)(args);
92    dtor.await;
93    match result {
94        Ok(result) => result,
95        Err(error) => std::panic::resume_unwind(error),
96    }
97}