use futures_core::ready; use pin_project_lite::pin_project; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use tower_service::Service; // Vendored from tower::util to reduce dependencies, the code is small enough. // Not really pub, but used in a trait for bounds pin_project! { #[project = OneshotProj] #[derive(Debug)] pub enum Oneshot, Req> { NotReady { svc: S, req: Option, }, Called { #[pin] fut: S::Future, }, Done, } } impl Oneshot where S: Service, { pub(crate) const fn new(svc: S, req: Req) -> Self { Oneshot::NotReady { svc, req: Some(req), } } } impl Future for Oneshot where S: Service, { type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { loop { let this = self.as_mut().project(); match this { OneshotProj::NotReady { svc, req } => { ready!(svc.poll_ready(cx))?; let fut = svc.call(req.take().expect("already called")); self.set(Oneshot::Called { fut }); } OneshotProj::Called { fut } => { let res = ready!(fut.poll(cx))?; self.set(Oneshot::Done); return Poll::Ready(Ok(res)); } OneshotProj::Done => panic!("polled after complete"), } } } }