engineioxide 0.17.3

Engine IO server implementation as a Tower Service.
Documentation
use crate::body::ResponseBody;
use crate::errors::Error;
use futures_core::ready;
use http::Response;
use pin_project_lite::pin_project;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};

pub(crate) type BoxFuture<B> =
    Pin<Box<dyn Future<Output = Result<Response<ResponseBody<B>>, Error>> + Send>>;

pin_project! {
    #[project = ResFutProj]
    pub enum ResponseFuture<F, B> {
        EmptyResponse {
            code: u16,
        },
        ReadyResponse {
            res: Option<Result<Response<ResponseBody<B>>, Error>>,
        },
        AsyncResponse {
            future: BoxFuture<B>,
        },
        Future {
            #[pin]
            future: F,
        },
    }
}

impl<F, B> ResponseFuture<F, B> {
    pub fn empty_response(code: u16) -> Self {
        ResponseFuture::EmptyResponse { code }
    }
    pub fn ready(res: Result<Response<ResponseBody<B>>, Error>) -> Self {
        ResponseFuture::ReadyResponse { res: Some(res) }
    }
    pub fn new(future: F) -> Self {
        ResponseFuture::Future { future }
    }
    pub fn async_response(future: BoxFuture<B>) -> Self {
        ResponseFuture::AsyncResponse { future }
    }
}

impl<ResBody, F, E> Future for ResponseFuture<F, ResBody>
where
    F: Future<Output = Result<Response<ResBody>, E>>,
{
    type Output = Result<Response<ResponseBody<ResBody>>, E>;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let res = match self.project() {
            ResFutProj::Future { future } => ready!(future.poll(cx))?.map(ResponseBody::new),

            ResFutProj::EmptyResponse { code } => Response::builder()
                .status(*code)
                .body(ResponseBody::empty_response())
                .unwrap(),
            ResFutProj::AsyncResponse { future } => ready!(
                future
                    .as_mut()
                    .poll(cx)
                    .map(|r| r.unwrap_or_else(|e| e.into()))
            ),
            ResFutProj::ReadyResponse { res } => res.take().unwrap().unwrap_or_else(|e| e.into()),
        };
        Poll::Ready(Ok(res))
    }
}