1use crate::{Policy, Retry};
4use futures_core::ready;
5use pin_project::{pin_project, project};
6use std::future::Future;
7use std::pin::Pin;
8use std::task::{Context, Poll};
9use tower_service::Service;
10
11#[pin_project]
13#[derive(Debug)]
14pub struct ResponseFuture<P, S, Request>
15where
16 P: Policy<Request, S::Response, S::Error>,
17 S: Service<Request>,
18{
19 request: Option<Request>,
20 #[pin]
21 retry: Retry<P, S>,
22 #[pin]
23 state: State<S::Future, P::Future>,
24}
25
26#[pin_project]
27#[derive(Debug)]
28enum State<F, P> {
29 Called(#[pin] F),
31 Checking(#[pin] P),
33 Retrying,
35}
36
37impl<P, S, Request> ResponseFuture<P, S, Request>
38where
39 P: Policy<Request, S::Response, S::Error>,
40 S: Service<Request>,
41{
42 pub(crate) fn new(
43 request: Option<Request>,
44 retry: Retry<P, S>,
45 future: S::Future,
46 ) -> ResponseFuture<P, S, Request> {
47 ResponseFuture {
48 request,
49 retry,
50 state: State::Called(future),
51 }
52 }
53}
54
55impl<P, S, Request> Future for ResponseFuture<P, S, Request>
56where
57 P: Policy<Request, S::Response, S::Error> + Clone,
58 S: Service<Request> + Clone,
59{
60 type Output = Result<S::Response, S::Error>;
61
62 #[project]
63 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
64 let mut this = self.project();
65
66 loop {
67 #[project]
68 match this.state.as_mut().project() {
69 State::Called(future) => {
70 let result = ready!(future.poll(cx));
71 if let Some(ref req) = this.request {
72 match this.retry.policy.retry(req, result.as_ref()) {
73 Some(checking) => {
74 this.state.set(State::Checking(checking));
75 }
76 None => return Poll::Ready(result),
77 }
78 } else {
79 return Poll::Ready(result);
81 }
82 }
83 State::Checking(future) => {
84 this.retry
85 .as_mut()
86 .project()
87 .policy
88 .set(ready!(future.poll(cx)));
89 this.state.set(State::Retrying);
90 }
91 State::Retrying => {
92 ready!(this.retry.as_mut().project().service.poll_ready(cx))?;
104 let req = this
105 .request
106 .take()
107 .expect("retrying requires cloned request");
108 *this.request = this.retry.policy.clone_request(&req);
109 this.state.set(State::Called(
110 this.retry.as_mut().project().service.call(req),
111 ));
112 }
113 }
114 }
115 }
116}