1

I'm doing some code training with warp and rust and I want to do something like the following:

let route = warp::path("my")
    .and(warp::path::param())
    .map(|filename: String| {
        match fs::read_to_string(filename) {
            Ok(value) => {
                warp::http::Response::builder()
                    .header("content-type", "text/html; charset=utf-8")
                    .body(value)
            },
            Err(_) => warp::redirect::see_other(Uri::from_static("/404"))
        }
    });

let to_404 = warp::path("404").map(|| "File not found!");

warp::serve(warp::get().and(to_404.or(route))).run(([127, 0, 0, 1], 3030)).await

the problem with this code is that the code won't compile because of two different return types in the same path. Is there a proper way to accomplish the behaviour I want? Thanks in advance!

TL;DR: I want to implement a path /my/:file that shows the content of an HTML file if exists or redirect to /404 in case of an error.

Edit: Added Uri::from_static and minor typos to get proper error at compilation.

error[E0308]: `match` arms have incompatible types
  --> src/main.rs:23:23
   |
17 |           match fs::read_to_string(filename) {
   |           ---------------------------------- `match` arms have incompatible types
18 |               Ok(value) => {
19 | /                 warp::http::Response::builder()
20 | |                     .header("content-type", "text/html; charset=utf-8")
21 | |                     .body(value)
   | |________________________________- this is found to be of type `Result<Response<String>, warp::http::Error>`
22 |               },
23 |               Err(_) => warp::redirect::see_other(Uri::from_static("/404"))
   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Result`, found opaque type
Esteban Zapata
  • 311
  • 2
  • 7

1 Answers1

1

I'm not sure if this the proper way to do it, but this is how I solve it (I don't like it, TBH).

I created two functions (a_response & a_redirect) that build a Response object:

use warp::{Filter, http::Response};

fn a_response(status: u16, content_type: &str, body: &str) -> Result<Response<String>, warp::http::Error> {
    warp::http::Response::builder()
        .header("content-type", content_type.to_string())
        .status(status)
        .body(body.to_string())
}

fn a_redirect(status: u16, location: &str) -> Result<Response<String>, warp::http::Error> {
    let final_status : u16;

    match status {
        301 | 302 | 303 | 307 | 308 => final_status = status,
        _ => final_status = 302
    }

    warp::http::Response::builder()
        .header("Location", location)
        .status(final_status)
        .body("".to_string())
}

then I just use it when needed:

let root = warp::path::end().map(|| {
        match fs::read_to_string("index.html") {
            Ok(body) => {
                a_response(200, "text/html; charset=utf-8", &body)
            },
            Err(_) => {
                a_redirect(303, "/500")
            }
        }
    });

Like I said, I don't like it, but it does the work.

Esteban Zapata
  • 311
  • 2
  • 7