error_rail/async_ext/
context_future.rs1use core::future::Future;
7use core::pin::Pin;
8use core::task::{Context, Poll};
9
10use futures_core::future::FusedFuture;
11
12use pin_project_lite::pin_project;
13
14use crate::traits::IntoErrorContext;
15use crate::types::ComposableError;
16
17pin_project! {
18 #[must_use = "futures do nothing unless polled"]
41 pub struct ContextFuture<Fut, F> {
42 #[pin]
43 future: Fut,
44 context_fn: Option<F>,
45 }
46}
47
48impl<Fut, F> ContextFuture<Fut, F> {
49 #[inline]
51 pub fn new(future: Fut, context_fn: F) -> Self {
52 Self { future, context_fn: Some(context_fn) }
53 }
54}
55
56impl<Fut, F, C, T, E> Future for ContextFuture<Fut, F>
57where
58 Fut: Future<Output = Result<T, E>>,
59 F: FnOnce() -> C,
60 C: IntoErrorContext,
61{
62 type Output = Result<T, ComposableError<E>>;
63
64 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
65 let this = self.project();
66
67 this.future.poll(cx).map(|res| {
68 res.map_err(|err| {
69 let context_fn = this
70 .context_fn
71 .take()
72 .expect("ContextFuture polled after completion; this is a bug");
73 ComposableError::new(err).with_context(context_fn())
74 })
75 })
76 }
77}
78
79impl<Fut, F, C, T, E> FusedFuture for ContextFuture<Fut, F>
80where
81 Fut: FusedFuture<Output = Result<T, E>>,
82 F: FnOnce() -> C,
83 C: IntoErrorContext,
84{
85 fn is_terminated(&self) -> bool {
86 self.context_fn.is_none() || self.future.is_terminated()
88 }
89}