Last active
December 1, 2021 08:49
-
-
Save meredian/d7ef7d049ecf5636fff12189fae6e10e to your computer and use it in GitHub Desktop.
How to properly pass Warp errors
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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