Warp filter function stacking problem

Hi!

I want to write a warp server that converts markdown files to HTML, but serves all other files from the file system directly. I'd also like to report an error if this fails.

I have tried writing two filters and a rejection handler, but I don't manage to put them together so it compiles, and I don't understand the error messages.

fn markdown_as_html(path: &str) -> Result<warp::reply::Html<std::string::String>, warp::Rejection> {
...
}
async fn handle_rejection(err: Rejection) -> Result<impl Reply, std::convert::Infallible> {
...
}
...
    let md_to_html =
        warp::path::full().map(|path: warp::path::FullPath| markdown_as_html(path.as_str()));

    // // serve static files
    let static_files = warp::fs::dir(repository_path.clone())
        // serve all static files
        .map(|reply: warp::filters::fs::File| {
            if let Some(name) = reply.path().file_name() {
                // filter  "dot" files
                if name.to_string_lossy().starts_with('.')
                {
                    Err(warp::reject::not_found)
                } else {
                    // serve file directly
                    Ok(reply)
                }
            } else {
                Err(warp::reject::not_found)
            }
        });

    let routes = md_to_html.or(static_files).recover(handle_rejection);
    let server = warp::serve(routes).try_bind(socket_address);

gives me

error[E0271]: type mismatch resolving `<warp::filter::map::Map<impl warp::filter::FilterBase<Extract = (warp::path::FullPath,), Error = std::convert::Infallible> + warp::Filter + std::marker::Copy, [closure@src/main.rs:84:28: 84:88]> as warp::filter::FilterBase>::Error == warp::Rejection`
  --> src/main.rs:83:6
   |
83 | ) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `warp::Rejection`, found enum `std::convert::Infallible`

error[E0277]: the trait bound `std::result::Result<warp::reply::Html<std::string::String>, warp::Rejection>: warp::Reply` is not satisfied
  --> src/main.rs:83:6
   |
83 | ) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `warp::Reply` is not implemented for `std::result::Result<warp::reply::Html<std::string::String>, warp::Rejection>`
   |
   = help: the following implementations were found:
             <std::result::Result<T, warp::http::Error> as warp::Reply>
   = note: required because of the requirements on the impl of `warp::Reply` for `(std::result::Result<warp::reply::Html<std::string::String>, warp::Rejection>,)`

error[E0271]: type mismatch resolving `<warp::filter::map::Map<impl warp::filter::FilterBase<Extract = (warp::path::FullPath,), Error = std::convert::Infallible> + warp::Filter + std::marker::Copy, [closure@src/main.rs:200:32: 200:92]> as warp::filter::FilterBase>::Error == warp::Rejection`
   --> src/main.rs:221:29
    |
221 |     let routes = md_to_html.or(static_files).recover(handle_rejection);
    |                             ^^ expected struct `warp::Rejection`, found enum `std::convert::Infallible`
    |
note: required by a bound in `warp::Filter::or`
   --> /Users/wiz/.cargo/registry/src/github.com-1ecc6299db9ec823/warp-0.3.2/src/filter/mod.rs:140:22
    |
140 |         Self: Filter<Error = Rejection> + Sized,
    |                      ^^^^^^^^^^^^^^^^^ required by this bound in `warp::Filter::or`

error[E0271]: type mismatch resolving `<warp::filter::or::Or<warp::filter::map::Map<impl warp::filter::FilterBase<Extract = (warp::path::FullPath,), Error = std::convert::Infallible> + warp::Filter + std::marker::Copy, [closure@src/main.rs:200:32: 200:92]>, warp::filter::map::Map<impl warp::filter::FilterBase<Extract = (warp::fs::File,), Error = warp::Rejection> + warp::filter::FilterClone, [closure@src/main.rs:205:14: 219:10]>> as warp::filter::FilterBase>::Error == warp::Rejection`
   --> src/main.rs:221:46
    |
221 |     let routes = md_to_html.or(static_files).recover(handle_rejection);
    |                                              ^^^^^^^ expected struct `warp::Rejection`, found enum `std::convert::Infallible`
    |
note: required by a bound in `warp::Filter::recover`
   --> /Users/wiz/.cargo/registry/src/github.com-1ecc6299db9ec823/warp-0.3.2/src/filter/mod.rs:294:22
    |
294 |         Self: Filter<Error = Rejection> + Sized,
    |                      ^^^^^^^^^^^^^^^^^ required by this bound in `warp::Filter::recover`

error[E0277]: the trait bound `fn(warp::Rejection) -> impl warp::Future<Output = std::result::Result<Opaque(DefId(0:48 ~ desqribe[3f3b]::handle_rejection::{opaque#0}::{opaque#0}), []), std::convert::Infallible>> {handle_rejection}: warp::generic::Func<std::convert::Infallible>` is not satisfied
   --> src/main.rs:227:30
    |
227 |     let server = warp::serve(routes).try_bind(socket_address);
    |                  ----------- ^^^^^^ the trait `warp::generic::Func<std::convert::Infallible>` is not implemented for `fn(warp::Rejection) -> impl warp::Future<Output = std::result::Result<Opaque(DefId(0:48 ~ desqribe[3f3b]::handle_rejection::{opaque#0}::{opaque#0}), []), std::convert::Infallible>> {handle_rejection}`
    |                  |
    |                  required by a bound introduced by this call
    |
    = note: required because of the requirements on the impl of `warp::filter::FilterBase` for `warp::filter::recover::Recover<warp::filter::or::Or<warp::filter::map::Map<impl warp::filter::FilterBase<Extract = (warp::path::FullPath,), Error = std::convert::Infallible> + warp::Filter + std::marker::Copy, [closure@src/main.rs:200:32: 200:92]>, warp::filter::map::Map<impl warp::filter::FilterBase<Extract = (warp::fs::File,), Error = warp::Rejection> + warp::filter::FilterClone, [closure@src/main.rs:205:14: 219:10]>>, fn(warp::Rejection) -> impl warp::Future<Output = std::result::Result<Opaque(DefId(0:48 ~ desqribe[3f3b]::handle_rejection::{opaque#0}::{opaque#0}), []), std::convert::Infallible>> {handle_rejection}>`

error[E0277]: the trait bound `std::result::Result<warp::reply::Html<std::string::String>, warp::Rejection>: warp::Reply` is not satisfied
   --> src/main.rs:227:18
    |
227 |     let server = warp::serve(routes).try_bind(socket_address);
    |                  ^^^^^^^^^^^ the trait `warp::Reply` is not implemented for `std::result::Result<warp::reply::Html<std::string::String>, warp::Rejection>`
    |
    = help: the following implementations were found:
              <std::result::Result<T, warp::http::Error> as warp::Reply>
    = note: required because of the requirements on the impl of `warp::Reply` for `(std::result::Result<warp::reply::Html<std::string::String>, warp::Rejection>,)`
    = note: 4 redundant requirements hidden
    = note: required because of the requirements on the impl of `warp::Reply` for `(warp::generic::Either<(warp::generic::Either<(std::result::Result<warp::reply::Html<std::string::String>, warp::Rejection>,), (std::result::Result<warp::fs::File, fn() -> warp::Rejection {warp::reject::not_found}>,)>,), (_,)>,)`
note: required by a bound in `warp::serve`
   --> /Users/wiz/.cargo/registry/src/github.com-1ecc6299db9ec823/warp-0.3.2/src/server.rs:26:17
    |
26  |     F::Extract: Reply,
    |                 ^^^^^ required by this bound in `warp::serve`

error[E0277]: the trait bound `std::result::Result<warp::fs::File, fn() -> warp::Rejection {warp::reject::not_found}>: warp::Reply` is not satisfied
   --> src/main.rs:227:18
    |
227 |     let server = warp::serve(routes).try_bind(socket_address);
    |                  ^^^^^^^^^^^ the trait `warp::Reply` is not implemented for `std::result::Result<warp::fs::File, fn() -> warp::Rejection {warp::reject::not_found}>`
    |
    = help: the following implementations were found:
              <std::result::Result<T, warp::http::Error> as warp::Reply>
    = note: required because of the requirements on the impl of `warp::Reply` for `(std::result::Result<warp::fs::File, fn() -> warp::Rejection {warp::reject::not_found}>,)`
    = note: 4 redundant requirements hidden
    = note: required because of the requirements on the impl of `warp::Reply` for `(warp::generic::Either<(warp::generic::Either<(std::result::Result<warp::reply::Html<std::string::String>, warp::Rejection>,), (std::result::Result<warp::fs::File, fn() -> warp::Rejection {warp::reject::not_found}>,)>,), (_,)>,)`
note: required by a bound in `warp::serve`
   --> /Users/wiz/.cargo/registry/src/github.com-1ecc6299db9ec823/warp-0.3.2/src/server.rs:26:17
    |
26  |     F::Extract: Reply,
    |                 ^^^^^ required by this bound in `warp::serve`

error[E0599]: the method `try_bind` exists for struct `warp::Server<warp::filter::recover::Recover<warp::filter::or::Or<warp::filter::map::Map<impl warp::filter::FilterBase<Extract = (warp::path::FullPath,), Error = std::convert::Infallible> + warp::Filter + std::marker::Copy, [closure@src/main.rs:200:32: 200:92]>, warp::filter::map::Map<impl warp::filter::FilterBase<Extract = (warp::fs::File,), Error = warp::Rejection> + warp::filter::FilterClone, [closure@src/main.rs:205:14: 219:10]>>, fn(warp::Rejection) -> impl warp::Future<Output = std::result::Result<Opaque(DefId(0:48 ~ desqribe[3f3b]::handle_rejection::{opaque#0}::{opaque#0}), []), std::convert::Infallible>> {handle_rejection}>>`, but its trait bounds were not satisfied
   --> src/main.rs:227:38
    |
227 |       let server = warp::serve(routes).try_bind(socket_address);
    |                                        ^^^^^^^^ method cannot be called on `warp::Server<warp::filter::recover::Recover<warp::filter::or::Or<warp::filter::map::Map<impl warp::filter::FilterBase<Extract = (warp::path::FullPath,), Error = std::convert::Infallible> + warp::Filter + std::marker::Copy, [closure@src/main.rs:200:32: 200:92]>, warp::filter::map::Map<impl warp::filter::FilterBase<Extract = (warp::fs::File,), Error = warp::Rejection> + warp::filter::FilterClone, [closure@src/main.rs:205:14: 219:10]>>, fn(warp::Rejection) -> impl warp::Future<Output = std::result::Result<Opaque(DefId(0:48 ~ desqribe[3f3b]::handle_rejection::{opaque#0}::{opaque#0}), []), std::convert::Infallible>> {handle_rejection}>>` due to unsatisfied trait bounds
    |
   ::: /Users/wiz/.cargo/registry/src/github.com-1ecc6299db9ec823/warp-0.3.2/src/filter/recover.rs:14:1
    |
14  |   pub struct Recover<T, F> {
    |   ------------------------ doesn't satisfy `_: warp::Filter`
...
41  | / pub struct RecoverFuture<T: Filter, F>
42  | | where
43  | |     T: Filter,
44  | |     F: Func<T::Error>,
...   |
50  | |     original_path_index: PathIndex,
51  | | }
    | | -
    | | |
    | |_doesn't satisfy `<_ as warp::Future>::Output = std::result::Result<_, _>`
    |   doesn't satisfy `_: warp::Future`
    |
    = note: the following trait bounds were not satisfied:
            `warp::filter::recover::Recover<warp::filter::or::Or<warp::filter::map::Map<impl warp::filter::FilterBase<Extract = (warp::path::FullPath,), Error = std::convert::Infallible> + warp::Filter + std::marker::Copy, [closure@src/main.rs:200:32: 200:92]>, warp::filter::map::Map<impl warp::filter::FilterBase<Extract = (warp::fs::File,), Error = warp::Rejection> + warp::filter::FilterClone, [closure@src/main.rs:205:14: 219:10]>>, fn(warp::Rejection) -> impl warp::Future<Output = std::result::Result<Opaque(DefId(0:48 ~ desqribe[3f3b]::handle_rejection::{opaque#0}::{opaque#0}), []), std::convert::Infallible>> {handle_rejection}>: warp::Filter`
            `fn(warp::Rejection) -> impl warp::Future<Output = std::result::Result<Opaque(DefId(0:48 ~ desqribe[3f3b]::handle_rejection::{opaque#0}::{opaque#0}), []), std::convert::Infallible>> {handle_rejection}: warp::generic::Func<std::convert::Infallible>`
            `<warp::filter::recover::RecoverFuture<warp::filter::or::Or<warp::filter::map::Map<impl warp::filter::FilterBase<Extract = (warp::path::FullPath,), Error = std::convert::Infallible> + warp::Filter + std::marker::Copy, [closure@src/main.rs:200:32: 200:92]>, warp::filter::map::Map<impl warp::filter::FilterBase<Extract = (warp::fs::File,), Error = warp::Rejection> + warp::filter::FilterClone, [closure@src/main.rs:205:14: 219:10]>>, fn(warp::Rejection) -> impl warp::Future<Output = std::result::Result<Opaque(DefId(0:48 ~ desqribe[3f3b]::handle_rejection::{opaque#0}::{opaque#0}), []), std::convert::Infallible>> {handle_rejection}> as warp::Future>::Output = std::result::Result<_, _>`
            `warp::filter::recover::RecoverFuture<warp::filter::or::Or<warp::filter::map::Map<impl warp::filter::FilterBase<Extract = (warp::path::FullPath,), Error = std::convert::Infallible> + warp::Filter + std::marker::Copy, [closure@src/main.rs:200:32: 200:92]>, warp::filter::map::Map<impl warp::filter::FilterBase<Extract = (warp::fs::File,), Error = warp::Rejection> + warp::filter::FilterClone, [closure@src/main.rs:205:14: 219:10]>>, fn(warp::Rejection) -> impl warp::Future<Output = std::result::Result<Opaque(DefId(0:48 ~ desqribe[3f3b]::handle_rejection::{opaque#0}::{opaque#0}), []), std::convert::Infallible>> {handle_rejection}>: warp::Future`

Any help very welcome!

Thanks,
Thomas

I still don't understand it completely, but I think the biggest problem was using 'map' in some places instead of 'and_then'.