ts_webapi/middleware/futures/
undefined.rs

1//! A future that is defined by the result of a future.
2
3use core::{
4    pin::Pin,
5    task::{Context, Poll, ready},
6};
7
8use http::{Request, Response, StatusCode};
9use http_body::Body;
10use pin_project_lite::pin_project;
11use tower_service::Service;
12
13/// Trait alias for the future that defined the final response.
14pub trait DefiningFuture<B>:
15    Future<Output = Result<Request<B>, StatusCode>> + Send + 'static
16{
17}
18impl<B, F> DefiningFuture<B> for F where
19    F: Future<Output = Result<Request<B>, StatusCode>> + Send + 'static
20{
21}
22
23pin_project! {
24    /// A future that is defined by the result of a future.
25    pub struct UndefinedFuture<S, B> where S: Service<Request<B>>, {
26        #[pin]
27        state: State<S::Future, Pin<Box<dyn DefiningFuture<B>>>>,
28        service: S,
29    }
30}
31
32pin_project! {
33    #[project = StateProj]
34    enum State<SFut, DFut> {
35        Defining {
36            #[pin]
37            future: DFut,
38        },
39        Proceeding {
40            #[pin]
41            future: SFut,
42        },
43    }
44}
45
46impl<S, B> UndefinedFuture<S, B>
47where
48    S: Service<Request<B>>,
49{
50    /// Define the response using the given future.
51    pub fn define(future: Pin<Box<dyn DefiningFuture<B>>>, service: S) -> Self {
52        Self {
53            state: State::Defining { future },
54            service,
55        }
56    }
57
58    /// Proceed with the service future.
59    pub fn proceed(future: S::Future, service: S) -> Self {
60        Self {
61            state: State::Proceeding { future },
62            service,
63        }
64    }
65}
66
67impl<ResBody, S, B> Future for UndefinedFuture<S, B>
68where
69    ResBody: Body + Default,
70    S: Service<Request<B>, Response = Response<ResBody>>,
71{
72    type Output = Result<Response<ResBody>, S::Error>;
73
74    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
75        let mut this = self.project();
76
77        loop {
78            match this.state.as_mut().project() {
79                StateProj::Defining { future } => {
80                    let auth = ready!(future.poll(cx));
81                    match auth {
82                        Ok(request) => {
83                            let future = this.service.call(request);
84                            this.state.set(State::Proceeding { future });
85                        }
86                        Err(status) => {
87                            return Poll::Ready(Ok(Response::builder()
88                                .status(status)
89                                .body(ResBody::default())
90                                .expect("error response should be valid response")));
91                        }
92                    };
93                }
94                StateProj::Proceeding { future: fut } => {
95                    return Poll::Ready(Ok(ready!(fut.poll(cx))?));
96                }
97            }
98        }
99    }
100}