error_stack/
future.rs

1#![expect(deprecated, reason = "We use `Context` to maintain compatibility")]
2//! Extension for convenient usage of [`Report`]s returned by [`Future`] s.
3//!
4//! Extends [`Future`] with the same methods as [`ResultExt`] but calls the methods on [`poll`]ing.
5//!
6//! [`Report`]: crate::Report
7//! [`poll`]: Future::poll
8
9use core::{
10    future::Future,
11    pin::Pin,
12    task::{Context as TaskContext, Poll},
13};
14
15use crate::{Attachment, Context, OpaqueAttachment, Report, ResultExt};
16
17macro_rules! implement_future_adaptor {
18    ($future:ident, $method:ident, $bound:ident $(+ $bounds:ident)* $(+ $lifetime:lifetime)*, $output:ty) => {
19        #[doc = concat!("Adaptor returned by [`FutureExt::", stringify!( $method ), "`].")]
20        pub struct $future<Fut, T> {
21            future: Fut,
22            inner: Option<T>,
23        }
24
25        impl<Fut, T> Future for $future<Fut, T>
26        where
27            Fut: Future,
28            Fut::Output: ResultExt,
29            T: $bound $(+ $bounds)* $(+ $lifetime)*
30        {
31            type Output = $output;
32
33            #[track_caller]
34            fn poll(self: Pin<&mut Self>, cx: &mut TaskContext) -> Poll<Self::Output> {
35                // SAFETY: The pointee of `inner` will not move. Note, that the value inside the
36                //         `Option` will be taken, but the `Option` remains in place. Additionally,
37                //         `Self` does not implement `Drop`, nor is it `#[repr(packed)]`
38                //         See the `pin` module: https://doc.rust-lang.org/core/pin/index.html
39                let (future, inner) = unsafe {
40                    let Self { future, inner } = self.get_unchecked_mut();
41                    (Pin::new_unchecked(future), inner)
42                };
43
44                // Can't use `map` as `#[track_caller]` is unstable on closures
45                match future.poll(cx) {
46                    Poll::Ready(value) => {
47                        Poll::Ready(value.$method({
48                            inner.take().expect("Cannot poll context after it resolves")
49                        }))
50                    }
51                    Poll::Pending => Poll::Pending,
52                }
53            }
54        }
55    };
56}
57
58macro_rules! implement_lazy_future_adaptor {
59    ($future:ident, $method:ident, $bound:ident $(+ $bounds:ident)* $(+ $lifetime:lifetime)*, $output:ty) => {
60        #[doc = concat!("Adaptor returned by [`FutureExt::", stringify!( $method ), "`].")]
61        pub struct $future<Fut, F> {
62            future: Fut,
63            inner: Option<F>,
64        }
65
66        impl<Fut, F, T> Future for $future<Fut, F>
67        where
68            Fut: Future,
69            Fut::Output: ResultExt,
70            F: FnOnce() -> T,
71            T: $bound $(+ $bounds)* $(+ $lifetime)*
72        {
73            type Output = $output;
74
75            #[track_caller]
76            fn poll(self: Pin<&mut Self>, cx: &mut TaskContext) -> Poll<Self::Output> {
77                // SAFETY: The pointee of `inner` will not move. Note, that the value inside the
78                //         `Option` will be taken, but the `Option` remains in place. Additionally,
79                //         `Self` does not implement `Drop`, nor is it `#[repr(packed)]`
80                //         See the `pin` module: https://doc.rust-lang.org/core/pin/index.html
81                let (future, inner) = unsafe {
82                    let Self { future, inner } = self.get_unchecked_mut();
83                    (Pin::new_unchecked(future), inner)
84                };
85
86                // Can't use `map` as `#[track_caller]` is unstable on closures
87                match future.poll(cx) {
88                    Poll::Ready(value) => {
89                        Poll::Ready(value.$method({
90                            inner.take().expect("Cannot poll context after it resolves")
91                        }))
92                    }
93                    Poll::Pending => Poll::Pending,
94                }
95            }
96        }
97    };
98}
99
100implement_future_adaptor!(
101    FutureWithAttachment,
102    attach,
103    Attachment,
104    Result<<Fut::Output as ResultExt>::Ok, Report<<Fut::Output as ResultExt>::Context>>
105);
106
107implement_lazy_future_adaptor!(
108    FutureWithLazyAttachment,
109    attach_with,
110    Attachment,
111    Result<<Fut::Output as ResultExt>::Ok, Report<<Fut::Output as ResultExt>::Context>>
112);
113
114implement_future_adaptor!(
115    FutureWithOpaqueAttachment,
116    attach_opaque,
117    OpaqueAttachment,
118    Result<<Fut::Output as ResultExt>::Ok, Report<<Fut::Output as ResultExt>::Context>>
119);
120
121implement_lazy_future_adaptor!(
122    FutureWithLazyOpaqueAttachment,
123    attach_opaque_with,
124    OpaqueAttachment,
125    Result<<Fut::Output as ResultExt>::Ok, Report<<Fut::Output as ResultExt>::Context>>
126);
127
128implement_future_adaptor!(
129    FutureWithContext,
130    change_context,
131    Context,
132    Result<<Fut::Output as ResultExt>::Ok, Report<T>>
133);
134
135implement_lazy_future_adaptor!(
136    FutureWithLazyContext,
137    change_context_lazy,
138    Context,
139    Result<<Fut::Output as ResultExt>::Ok, Report<T>>
140);
141
142/// Extension trait for [`Future`] to provide contextual information on [`Report`]s.
143///
144/// [`Report`]: crate::Report
145pub trait FutureExt: Future + Sized {
146    /// Adds a new printable attachment to the [`Report`] inside the [`Result`] when [`poll`]ing the
147    /// [`Future`].
148    ///
149    /// Applies [`Report::attach`] on the [`Err`] variant, refer to it for more
150    /// information.
151    ///
152    /// [`Report`]: crate::Report
153    /// [`Report::attach`]: crate::Report::attach
154    /// [`poll`]: Future::poll
155    #[track_caller]
156    fn attach<A>(self, attachment: A) -> FutureWithAttachment<Self, A>
157    where
158        A: Attachment;
159
160    /// Lazily adds a new printable attachment to the [`Report`] inside the [`Result`] when
161    /// [`poll`]ing the [`Future`].
162    ///
163    /// Applies [`Report::attach`] on the [`Err`] variant, refer to it for more
164    /// information.
165    ///
166    /// [`Report`]: crate::Report
167    /// [`Report::attach`]: crate::Report::attach
168    /// [`poll`]: Future::poll
169    #[track_caller]
170    fn attach_with<A, F>(self, attachment: F) -> FutureWithLazyAttachment<Self, F>
171    where
172        A: Attachment,
173        F: FnOnce() -> A;
174
175    #[deprecated(
176        note = "Use `attach_opaque_with` instead. `attach_lazy` was renamed to \
177                `attach_opaque_with` and `attach_printable_lazy` was renamed to `attach_with`",
178        since = "0.6.0"
179    )]
180    #[track_caller]
181    fn attach_lazy<A, F>(self, attachment: F) -> FutureWithLazyOpaqueAttachment<Self, F>
182    where
183        A: OpaqueAttachment,
184        F: FnOnce() -> A;
185
186    /// Adds a new attachment to the [`Report`] inside the [`Result`] when [`poll`]ing the
187    /// [`Future`].
188    ///
189    /// Applies [`Report::attach_opaque`] on the [`Err`] variant, refer to it for more information.
190    ///
191    /// [`Report`]: crate::Report
192    /// [`Report::attach_opaque`]: crate::Report::attach_opaque
193    /// [`poll`]: Future::poll
194    #[track_caller]
195    fn attach_opaque<A>(self, attachment: A) -> FutureWithOpaqueAttachment<Self, A>
196    where
197        A: OpaqueAttachment;
198
199    /// Lazily adds a new attachment to the [`Report`] inside the [`Result`] when [`poll`]ing the
200    /// [`Future`].
201    ///
202    /// Applies [`Report::attach_opaque`] on the [`Err`] variant, refer to it for more information.
203    ///
204    /// [`Report`]: crate::Report
205    /// [`Report::attach_opaque`]: crate::Report::attach_opaque
206    /// [`poll`]: Future::poll
207    #[track_caller]
208    fn attach_opaque_with<A, F>(self, attachment: F) -> FutureWithLazyOpaqueAttachment<Self, F>
209    where
210        A: OpaqueAttachment,
211        F: FnOnce() -> A;
212
213    #[track_caller]
214    #[deprecated(
215        note = "Use `attach` instead. `attach` was renamed to `attach_opaque` and \
216                `attach_printable` was renamed to `attach`",
217        since = "0.6.0"
218    )]
219    #[inline]
220    fn attach_printable<A>(self, attachment: A) -> FutureWithAttachment<Self, A>
221    where
222        A: Attachment,
223    {
224        self.attach(attachment)
225    }
226
227    #[track_caller]
228    #[deprecated(
229        note = "Use `attach_with` instead. `attach_lazy` was renamed to `attach_opaque_with` and \
230                `attach_printable_lazy` was renamed to `attach_with`",
231        since = "0.6.0"
232    )]
233    #[inline]
234    fn attach_printable_lazy<A, F>(self, attachment: F) -> FutureWithLazyAttachment<Self, F>
235    where
236        A: Attachment,
237        F: FnOnce() -> A,
238    {
239        self.attach_with(attachment)
240    }
241
242    /// Changes the [`Context`] of the [`Report`] inside the [`Result`] when [`poll`]ing the
243    /// [`Future`].
244    ///
245    /// Applies [`Report::change_context`] on the [`Err`] variant, refer to it for more information.
246    ///
247    /// [`Report`]: crate::Report
248    /// [`Report::change_context`]: crate::Report::change_context
249    /// [`poll`]: Future::poll
250    #[track_caller]
251    fn change_context<C>(self, context: C) -> FutureWithContext<Self, C>
252    where
253        C: Context;
254
255    /// Lazily changes the [`Context`] of the [`Report`] inside the [`Result`] when [`poll`]ing the
256    /// [`Future`].
257    ///
258    /// Applies [`Report::change_context`] on the [`Err`] variant, refer to it for more information.
259    ///
260    /// [`Report`]: crate::Report
261    /// [`Report::change_context`]: crate::Report::change_context
262    /// [`poll`]: Future::poll
263    #[track_caller]
264    fn change_context_lazy<C, F>(self, context: F) -> FutureWithLazyContext<Self, F>
265    where
266        C: Context,
267        F: FnOnce() -> C;
268}
269
270impl<Fut: Future> FutureExt for Fut
271where
272    Fut::Output: ResultExt,
273{
274    #[track_caller]
275    fn attach<A>(self, attachment: A) -> FutureWithAttachment<Self, A>
276    where
277        A: Attachment,
278    {
279        FutureWithAttachment {
280            future: self,
281            inner: Some(attachment),
282        }
283    }
284
285    #[track_caller]
286    fn attach_with<A, F>(self, attachment: F) -> FutureWithLazyAttachment<Self, F>
287    where
288        A: Attachment,
289        F: FnOnce() -> A,
290    {
291        FutureWithLazyAttachment {
292            future: self,
293            inner: Some(attachment),
294        }
295    }
296
297    #[track_caller]
298    fn attach_lazy<A, F>(self, attachment: F) -> FutureWithLazyOpaqueAttachment<Self, F>
299    where
300        A: OpaqueAttachment,
301        F: FnOnce() -> A,
302    {
303        FutureWithLazyOpaqueAttachment {
304            future: self,
305            inner: Some(attachment),
306        }
307    }
308
309    fn attach_opaque<A>(self, attachment: A) -> FutureWithOpaqueAttachment<Self, A>
310    where
311        A: OpaqueAttachment,
312    {
313        FutureWithOpaqueAttachment {
314            future: self,
315            inner: Some(attachment),
316        }
317    }
318
319    #[track_caller]
320    fn attach_opaque_with<A, F>(self, attachment: F) -> FutureWithLazyOpaqueAttachment<Self, F>
321    where
322        A: OpaqueAttachment,
323        F: FnOnce() -> A,
324    {
325        FutureWithLazyOpaqueAttachment {
326            future: self,
327            inner: Some(attachment),
328        }
329    }
330
331    #[track_caller]
332    fn change_context<C>(self, context: C) -> FutureWithContext<Self, C>
333    where
334        C: Context,
335    {
336        FutureWithContext {
337            future: self,
338            inner: Some(context),
339        }
340    }
341
342    #[track_caller]
343    fn change_context_lazy<C, F>(self, context: F) -> FutureWithLazyContext<Self, F>
344    where
345        C: Context,
346        F: FnOnce() -> C,
347    {
348        FutureWithLazyContext {
349            future: self,
350            inner: Some(context),
351        }
352    }
353}