1// TODO: Eventually to be replaced with tower_util::Oneshot.
2
3use std::future::Future;
4use std::pin::Pin;
5use std::task::{Context, Poll};
6
7use pin_project_lite::pin_project;
8use tower_service::Service;
9
10pub(crate) fn oneshot<S, Req>(svc: S, req: Req) -> Oneshot<S, Req>
11where
12 S: Service<Req>,
13{
14 Oneshot {
15 state: State::NotReady { svc, req },
16 }
17}
18
19pin_project! {
20 // A `Future` consuming a `Service` and request, waiting until the `Service`
21 // is ready, and then calling `Service::call` with the request, and
22 // waiting for that `Future`.
23 #[allow(missing_debug_implementations)]
24 pub struct Oneshot<S: Service<Req>, Req> {
25 #[pin]
26 state: State<S, Req>,
27 }
28}
29
30pin_project! {
31 #[project = StateProj]
32 #[project_replace = StateProjOwn]
33 enum State<S: Service<Req>, Req> {
34 NotReady {
35 svc: S,
36 req: Req,
37 },
38 Called {
39 #[pin]
40 fut: S::Future,
41 },
42 Tmp,
43 }
44}
45
46impl<S, Req> Future for Oneshot<S, Req>
47where
48 S: Service<Req>,
49{
50 type Output = Result<S::Response, S::Error>;
51
52 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
53 let mut me = self.project();
54
55 loop {
56 match me.state.as_mut().project() {
57 StateProj::NotReady { ref mut svc, .. } => {
58 ready!(svc.poll_ready(cx))?;
59 // fallthrough out of the match's borrow
60 }
61 StateProj::Called { fut } => {
62 return fut.poll(cx);
63 }
64 StateProj::Tmp => unreachable!(),
65 }
66
67 match me.state.as_mut().project_replace(State::Tmp) {
68 StateProjOwn::NotReady { mut svc, req } => {
69 me.state.set(State::Called { fut: svc.call(req) });
70 }
71 _ => unreachable!(),
72 }
73 }
74 }
75}
76