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 const 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 #[inline]
65 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
66 let this = self.project();
67
68 match this.future.poll(cx) {
69 Poll::Pending => Poll::Pending,
70 Poll::Ready(Ok(value)) => Poll::Ready(Ok(value)),
71 Poll::Ready(Err(err)) => {
72 let context_fn = this
74 .context_fn
75 .take()
76 .expect("ContextFuture polled after completion");
77 Poll::Ready(Err(ComposableError::new(err).with_context(context_fn())))
78 },
79 }
80 }
81}
82
83impl<Fut, F, C, T, E> FusedFuture for ContextFuture<Fut, F>
84where
85 Fut: FusedFuture<Output = Result<T, E>>,
86 F: FnOnce() -> C,
87 C: IntoErrorContext,
88{
89 #[inline]
90 fn is_terminated(&self) -> bool {
91 self.context_fn.is_none() || self.future.is_terminated()
92 }
93}