Skip to main content

hyper_util/service/
oneshot.rs

1use pin_project_lite::pin_project;
2use std::pin::Pin;
3use std::task::{Context, Poll, ready};
4use tower_service::Service;
5
6// Vendored from tower::util to reduce dependencies, the code is small enough.
7
8// Not really pub, but used in a trait for bounds
9pin_project! {
10    #[project = OneshotProj]
11    #[derive(Debug)]
12    pub enum Oneshot<S: Service<Req>, Req> {
13        NotReady {
14            svc: S,
15            req: Option<Req>,
16        },
17        Called {
18            #[pin]
19            fut: S::Future,
20        },
21        Done,
22    }
23}
24
25impl<S, Req> Oneshot<S, Req>
26where
27    S: Service<Req>,
28{
29    pub(crate) const fn new(svc: S, req: Req) -> Self {
30        Oneshot::NotReady {
31            svc,
32            req: Some(req),
33        }
34    }
35}
36
37impl<S, Req> Future for Oneshot<S, Req>
38where
39    S: Service<Req>,
40{
41    type Output = Result<S::Response, S::Error>;
42
43    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
44        loop {
45            let this = self.as_mut().project();
46            match this {
47                OneshotProj::NotReady { svc, req } => {
48                    ready!(svc.poll_ready(cx))?;
49                    let fut = svc.call(req.take().expect("already called"));
50                    self.set(Oneshot::Called { fut });
51                }
52                OneshotProj::Called { fut } => {
53                    let res = ready!(fut.poll(cx))?;
54                    self.set(Oneshot::Done);
55                    return Poll::Ready(Ok(res));
56                }
57                OneshotProj::Done => panic!("polled after complete"),
58            }
59        }
60    }
61}