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