modio/response/
future.rs

1use std::future::Future;
2use std::marker::PhantomData;
3use std::pin::Pin;
4use std::task::{Context, Poll};
5
6use http::{header, StatusCode};
7
8use super::{ErrorResponseFuture, Output, Response};
9use crate::client;
10use crate::error::{self, Error};
11
12/// A `Future` that will resolve to a [`Response`].
13pub struct ResponseFuture<T> {
14    state: State,
15    phantom: PhantomData<T>,
16}
17
18enum State {
19    InFlight(client::service::ResponseFuture),
20    ErrorResponse(StatusCode, ErrorResponseFuture),
21    Failed(Error),
22    Completed,
23}
24
25impl<T> ResponseFuture<T> {
26    pub(crate) fn new(fut: client::service::ResponseFuture) -> Self {
27        Self {
28            state: State::InFlight(fut),
29            phantom: PhantomData,
30        }
31    }
32
33    pub(crate) fn failed(error: Error) -> Self {
34        Self {
35            state: State::Failed(error),
36            phantom: PhantomData,
37        }
38    }
39}
40
41impl<T: Unpin> Future for ResponseFuture<T> {
42    type Output = Output<T>;
43
44    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
45        loop {
46            let state = std::mem::replace(&mut self.state, State::Completed);
47
48            match state {
49                State::InFlight(mut fut) => {
50                    let resp = match Pin::new(&mut fut).poll(cx) {
51                        Poll::Ready(Ok(resp)) => Response::new(resp),
52                        Poll::Ready(Err(err)) => return Poll::Ready(Err(error::request(err))),
53                        Poll::Pending => {
54                            self.state = State::InFlight(fut);
55                            return Poll::Pending;
56                        }
57                    };
58
59                    if resp.status().is_success() {
60                        return Poll::Ready(Ok(resp));
61                    }
62
63                    let retry_after = resp
64                        .headers()
65                        .get(header::RETRY_AFTER)
66                        .and_then(|v| v.to_str().ok())
67                        .and_then(|v| v.parse().ok());
68
69                    if let Some(retry_after) = retry_after {
70                        return Poll::Ready(Err(error::ratelimit(retry_after)));
71                    }
72
73                    self.state = State::ErrorResponse(resp.status(), resp.error());
74                }
75                State::ErrorResponse(status, mut fut) => {
76                    let error = match Pin::new(&mut fut).poll(cx) {
77                        Poll::Ready(Ok(resp)) => resp.error,
78                        Poll::Ready(Err(err)) => return Poll::Ready(Err(error::request(err))),
79                        Poll::Pending => {
80                            self.state = State::ErrorResponse(status, fut);
81                            return Poll::Pending;
82                        }
83                    };
84
85                    return Poll::Ready(Err(error::error_for_status(status, error)));
86                }
87                State::Failed(err) => return Poll::Ready(Err(err)),
88                State::Completed => panic!("future is already completed"),
89            }
90        }
91    }
92}