1use core::{
9 error::Error,
10 future::Future,
11 pin::Pin,
12 task::{Context as TaskContext, Poll},
13};
14
15use crate::{Attachment, 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 let (future, inner) = unsafe {
40 let Self { future, inner } = self.get_unchecked_mut();
41 (Pin::new_unchecked(future), inner)
42 };
43
44 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 let (future, inner) = unsafe {
82 let Self { future, inner } = self.get_unchecked_mut();
83 (Pin::new_unchecked(future), inner)
84 };
85
86 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 Error + Send + Sync + 'static,
132 Result<<Fut::Output as ResultExt>::Ok, Report<T>>
133);
134
135implement_lazy_future_adaptor!(
136 FutureWithLazyContext,
137 change_context_lazy,
138 Error + Send + Sync + 'static,
139 Result<<Fut::Output as ResultExt>::Ok, Report<T>>
140);
141
142pub trait FutureExt: Future + Sized {
146 #[track_caller]
156 fn attach<A>(self, attachment: A) -> FutureWithAttachment<Self, A>
157 where
158 A: Attachment;
159
160 #[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 #[track_caller]
184 fn attach_opaque<A>(self, attachment: A) -> FutureWithOpaqueAttachment<Self, A>
185 where
186 A: OpaqueAttachment;
187
188 #[track_caller]
197 fn attach_opaque_with<A, F>(self, attachment: F) -> FutureWithLazyOpaqueAttachment<Self, F>
198 where
199 A: OpaqueAttachment,
200 F: FnOnce() -> A;
201
202 #[track_caller]
211 fn change_context<C>(self, context: C) -> FutureWithContext<Self, C>
212 where
213 C: Error + Send + Sync + 'static;
214
215 #[track_caller]
224 fn change_context_lazy<C, F>(self, context: F) -> FutureWithLazyContext<Self, F>
225 where
226 C: Error + Send + Sync + 'static,
227 F: FnOnce() -> C;
228}
229
230impl<Fut: Future> FutureExt for Fut
231where
232 Fut::Output: ResultExt,
233{
234 #[track_caller]
235 fn attach<A>(self, attachment: A) -> FutureWithAttachment<Self, A>
236 where
237 A: Attachment,
238 {
239 FutureWithAttachment {
240 future: self,
241 inner: Some(attachment),
242 }
243 }
244
245 #[track_caller]
246 fn attach_with<A, F>(self, attachment: F) -> FutureWithLazyAttachment<Self, F>
247 where
248 A: Attachment,
249 F: FnOnce() -> A,
250 {
251 FutureWithLazyAttachment {
252 future: self,
253 inner: Some(attachment),
254 }
255 }
256
257 fn attach_opaque<A>(self, attachment: A) -> FutureWithOpaqueAttachment<Self, A>
258 where
259 A: OpaqueAttachment,
260 {
261 FutureWithOpaqueAttachment {
262 future: self,
263 inner: Some(attachment),
264 }
265 }
266
267 #[track_caller]
268 fn attach_opaque_with<A, F>(self, attachment: F) -> FutureWithLazyOpaqueAttachment<Self, F>
269 where
270 A: OpaqueAttachment,
271 F: FnOnce() -> A,
272 {
273 FutureWithLazyOpaqueAttachment {
274 future: self,
275 inner: Some(attachment),
276 }
277 }
278
279 #[track_caller]
280 fn change_context<C>(self, context: C) -> FutureWithContext<Self, C>
281 where
282 C: Error + Send + Sync + 'static,
283 {
284 FutureWithContext {
285 future: self,
286 inner: Some(context),
287 }
288 }
289
290 #[track_caller]
291 fn change_context_lazy<C, F>(self, context: F) -> FutureWithLazyContext<Self, F>
292 where
293 C: Error + Send + Sync + 'static,
294 F: FnOnce() -> C,
295 {
296 FutureWithLazyContext {
297 future: self,
298 inner: Some(context),
299 }
300 }
301}