//! Future types for the `Batch` middleware. use super::{error::Closed, message}; use futures_core::ready; use pin_project::pin_project; use std::{ fmt::Debug, future::Future, pin::Pin, task::{Context, Poll}, }; use tower::Service; /// Future that completes when the batch processing is complete. #[pin_project] pub struct ResponseFuture where S: Service>, { #[pin] state: ResponseState, } impl Debug for ResponseFuture where S: Service>, S::Future: Debug, S::Error: Debug, E2: Debug, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ResponseFuture") .field("state", &self.state) .finish() } } #[pin_project(project = ResponseStateProj)] enum ResponseState where S: Service>, { Failed(Option), Rx(#[pin] message::Rx), Poll(#[pin] S::Future), } impl Debug for ResponseState where S: Service>, S::Future: Debug, S::Error: Debug, E2: Debug, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { ResponseState::Failed(e) => f.debug_tuple("ResponseState::Failed").field(e).finish(), ResponseState::Rx(rx) => f.debug_tuple("ResponseState::Rx").field(rx).finish(), ResponseState::Poll(fut) => f.debug_tuple("ResponseState::Pool").field(fut).finish(), } } } impl ResponseFuture where S: Service>, { pub(crate) fn new(rx: message::Rx) -> Self { ResponseFuture { state: ResponseState::Rx(rx), } } pub(crate) fn failed(err: E2) -> Self { ResponseFuture { state: ResponseState::Failed(Some(err)), } } } impl Future for ResponseFuture where S: Service>, S::Future: Future>, S::Error: Into, crate::error::Closed: Into, { type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.project(); loop { match this.state.as_mut().project() { ResponseStateProj::Failed(e) => { return Poll::Ready(Err(e.take().expect("polled after error"))); } ResponseStateProj::Rx(rx) => match ready!(rx.poll(cx)) { Ok(Ok(f)) => this.state.set(ResponseState::Poll(f)), Ok(Err(e)) => return Poll::Ready(Err(e.into())), Err(_) => return Poll::Ready(Err(Closed::new().into())), }, ResponseStateProj::Poll(fut) => return fut.poll(cx).map_err(Into::into), } } } }