Combine a list of Warp filters

I am starting with Rust, and maybe I am trying to solve this in a wrong way, but I can't figure out how to dynamically compose multiple Warp filters into a single one.

Example:

// These could come from some command line args or maybe a config file
let apps = vec!["app1", "app2", "app3"];

let routes = apps
    .into_iter()
    .map(|app| {
        warp::get()
            .and(warp::path("apps"))
            .and(warp::path(app))
            .map(warp::reply)
    })
    .fold(warp::any(), |routes, route| routes.or(route));

warp::serve(routes)
    .run(([127, 0, 0, 1], 3000))
    .await;

I can't make .fold to type check. Am I trying something absurd here?

1 Like

The type produced by routes.or(route) recursively contains the types of routes and route, meaning it will be different for every route appended. You should be able to box the filters to make them all the same type:

.fold(warp::any().boxed(), |routes, route| routes.or(route).boxed());

This doesn't really answer your question, but that code sample can also be structured like this FYI.

    let routes = warp::path("apps")
        .and(
            warp::path("app1")
                .or(warp::path("app2"))
                .unify()
                .or(warp::path("app3"))
                .unify(),
        )
        .map(warp::reply);

    warp::serve(routes).run(([127, 0, 0, 1], 3000)).await;

Thanks a lot! I didn't know the boxed method! Anyway with your suggestion and drewkett's I was able to make it type check:

let routes = apps
    .into_iter()
    .map(|app| {
        warp::get()
            .and(warp::path("apps"))
            .and(warp::path(app))
            .map(warp::reply)
            .boxed()
    })
    .fold(warp::get("impossible").map(warp::reply).boxed(), |routes, route| routes.or(route).unify().boxed());

Thanks for the suggestion. But the point is that I will only known the "apps" list during runtime.

I figured it was something like that. Here's another way to do it in case its useful

let routes = warp::path("apps")
        .and(
            warp::path::param()
                .and_then(move |app: String| {
                    if apps.contains(&app.as_str()) {
                        ready(Ok(()))
                    } else {
                        ready(Err(warp::reject::not_found()))
                    }
                })
                .untuple_one(),
        )
        .map(warp::reply);

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.