Skip to content

Instantly share code, notes, and snippets.

@meredian
Last active December 1, 2021 08:49
Show Gist options
  • Save meredian/d7ef7d049ecf5636fff12189fae6e10e to your computer and use it in GitHub Desktop.
Save meredian/d7ef7d049ecf5636fff12189fae6e10e to your computer and use it in GitHub Desktop.
How to properly pass Warp errors
// As stated by author, you shouldn't use rejection as "return value" for
// error, since rejection mostly means "This filter is not capable of handling
// this request". So other filters should be tried.
// https://github.com/seanmonstar/warp/issues/388#issuecomment-576453485
//
// So whatever you return (Ok or Error), it should be a response value. Implementing
// that was not obvious, however Warp `3.2` added an approach to solve that.
// Operation `map` was added to cast whatever's on the chain into response, and we
// can use it to convert our custom error into Reply, not Rejection, to make warp
// work properly
// Custom error
#[derive(Debug)]
pub enum MyError {
MyClientError(String)
MyInternalError(String)
}
// Make this error castable to reply
impl Reply for Error {
fn into_response(self) -> reply::Response {
let (code, message) = map_error(&self);
return warp::reply::with_status(message, code).into_response()
}
}
// Build a way to map it to proper status codes
fn map_error(err: &Error) -> (StatusCode, &'static str) {
match err {
Error::MyClientError(_) => (StatusCode::NOT_FOUND, "User not found"),
_ => {
eprintln!("unhandled application error: {:?}", err);
(StatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error")
}
}
}
// This function works only if both T & E implement Reply, so it generates Response
// object from that. We will pass our
fn to_reply<T: Reply, E: Reply>(res: Result<T, E>) -> impl Reply {
match res {
Ok(r) => r.into_response(),
Err(e) => e.into_response(),
}
}
// For handler we return whatever type we want
pub async fn ok_handler() -> Result<impl Reply, MyError> {
Ok("It's all fine")
}
pub async fn err_handler() -> Result<impl Reply, MyError> {
Err(MyError::MyClientError("You have a problem here"))
}
pub fn router(
db_pool: &DBPool,
) -> impl warp::Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
let ok_router = warp::path!("ok")
.and(warp::get())
.then(ok_handler) // So, then can have ANY return type
.map(to_reply); // `map` transforms it to `impl Reply`, and `warp` accepts it.
let err_router = warp::path!("err")
.and(warp::get())
.then(err_handler)
.map(to_reply);
ok_router.or(err_router)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment