Repository: actix/actix-web
File: src/middleware/err_handlers.rs (Rust)

Code before:
        handlers: Handlers<B>,
    },
    HandlerFuture {
        fut: LocalBoxFuture<'static, Fut::Output>,
    },
}

impl<Fut, B> Future for ErrorHandlersFuture<Fut, B>
where
    Fut: Future<Output = Result<ServiceResponse<B>, Error>>,
{
    type Output = Fut::Output;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        loop {
            match self.as_mut().project() {
                ErrorHandlersProj::ServiceFuture { fut, handlers } => {
                    let res = ready!(fut.poll(cx))?;
                    match handlers.get(&res.status()) {
                        Some(handler) => match handler(res)? {
                            ErrorHandlerResponse::Response(res) => {
                                return Poll::Ready(Ok(res))
                            }
                            ErrorHandlerResponse::Future(fut) => self
                                .as_mut()
                                .set(ErrorHandlersFuture::HandlerFuture { fut }),
                        },
                        None => return Poll::Ready(Ok(res)),
                    }
                }
                ErrorHandlersProj::HandlerFuture { fut } => {
                    return fut.as_mut().poll(cx)
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use actix_service::IntoService;
    use futures_util::future::{ok, FutureExt};

    use super::*;
    use crate::http::{header::CONTENT_TYPE, HeaderValue, StatusCode};
    use crate::test::{self, TestRequest};
    use crate::HttpResponse;

    #[allow(clippy::unnecessary_wraps)]
    fn render_500<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
        res.response_mut()

Review comment (suggestion):
from a quick skim seems like this is the only branch that breaks out if the loop and would be a bit clearer if it could be unwrapped from the loop and call self.poll() instead

Code after:
        handlers: Handlers<B>,
    },
    HandlerFuture {
        fut: LocalBoxFuture<'static, Fut::Output>,
    },
}

impl<Fut, B> Future for ErrorHandlersFuture<Fut, B>
where
    Fut: Future<Output = Result<ServiceResponse<B>, Error>>,
{
    type Output = Fut::Output;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        match self.as_mut().project() {
            ErrorHandlersProj::ServiceFuture { fut, handlers } => {
                let res = ready!(fut.poll(cx))?;
                match handlers.get(&res.status()) {
                    Some(handler) => match handler(res)? {
                        ErrorHandlerResponse::Response(res) => Poll::Ready(Ok(res)),
                        ErrorHandlerResponse::Future(fut) => {
                            self.as_mut()
                                .set(ErrorHandlersFuture::HandlerFuture { fut });
                            self.poll(cx)
                        }
                    },
                    None => Poll::Ready(Ok(res)),
                }
            }
            ErrorHandlersProj::HandlerFuture { fut } => fut.as_mut().poll(cx),
        }
    }
}

#[cfg(test)]
mod tests {
    use actix_service::IntoService;
    use futures_util::future::{ok, FutureExt};

    use super::*;
    use crate::http::{header::CONTENT_TYPE, HeaderValue, StatusCode};
    use crate::test::{self, TestRequest};
    use crate::HttpResponse;

    #[allow(clippy::unnecessary_wraps)]
    fn render_500<B>(mut res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
        res.response_mut()
            .headers_mut()
            .insert(CONTENT_TYPE, HeaderValue::from_static("0001"));
        Ok(ErrorHandlerResponse::Response(res))
    }