Search code examples
rustrust-axum

How create signature for return type when wrapping complex library func?


I have a problem with creating a return type for the Axum middleware creation function.

Ex. I have middleware with a signature:

async fn auth_middleware(headers: HeaderMap, request: Request, next: Next) -> Result<Response, StatusCode> 

I want to create a function in my crate that returns ready-to-add middleware for Axum, but I cannot figure out the return type of wrapping fn.

pub fn axum_auth_middleware() -> ??? {
    axum::middleware::from_fn(auth_middleware)
}

I'm pretty new to Rust and I spent an hour to solve this but I gave up.


Solution

  • Probably the best way is to define a dummy Fn trait (MyFn) where Output can be named and then specify the return type as a FromFnLayer where the function is impl MyFn<..., Output: Future + ...> + ...:

    use axum::body::Body;
    use axum::extract::Request;
    use axum::middleware::FromFnLayer;
    use axum::middleware::Next;
    use axum::response::Response;
    use axum::Router;
    use http::header::HeaderMap;
    use http::status::StatusCode;
    use std::future::Future;
    
    async fn auth_middleware(
        headers: HeaderMap,
        request: Request,
        next: Next,
    ) -> Result<Response, StatusCode> {
        todo!();
    }
    
    pub trait MyFn<A, B, C>: Fn(A, B, C) -> <Self as MyFn<A, B, C>>::Output {
        type Output;
    }
    
    impl<F, A, B, C, Out> MyFn<A, B, C> for F
    where
        F: Fn(A, B, C) -> Out,
    {
        type Output = Out;
    }
    
    pub fn axum_auth_middleware() -> FromFnLayer<
        impl MyFn<
                HeaderMap,
                http::Request<Body>,
                Next,
                Output: Future<Output = Result<Response<Body>, StatusCode>> + Send + 'static,
            > + Clone
            + Copy
            + Send
            + Sync
            + 'static,
        (),
        (HeaderMap, http::Request<Body>),
    > {
        axum::middleware::from_fn(auth_middleware)
    }
    
    fn main() {
        let app = Router::<()>::new().route_layer(axum_auth_middleware());
    }