diff --git a/Cargo.toml b/Cargo.toml index 121c05408..22727eed5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ edition = "2018" all-features = true [dependencies] +anyhow = { version = "1", optional = true } async-compression = { version = "0.3.1", features = ["brotli", "deflate", "gzip", "stream"], optional = true } bytes = "0.5" futures = { version = "0.3", default-features = false, features = ["alloc"] } @@ -83,3 +84,7 @@ required-features = ["websocket"] [[example]] name = "websockets_chat" required-features = ["websocket"] + +[[example]] +name = "anyhow" +required-features = ["anyhow"] diff --git a/examples/anyhow.rs b/examples/anyhow.rs new file mode 100644 index 000000000..60dbf63c0 --- /dev/null +++ b/examples/anyhow.rs @@ -0,0 +1,61 @@ +#![deny(warnings)] + +use serde::export::Formatter; +use warp::http::StatusCode; +use warp::{Filter, Rejection, Reply}; + +#[tokio::main] +async fn main() { + let is_even = warp::path!("is_even" / u64).and_then(handler); + + let routes = warp::get().and(is_even).recover(handle_rejection); + + warp::serve(routes).run(([127, 0, 0, 1], 3030)).await; +} + +async fn handler(number: u64) -> anyhow::Result { + if number > 100 { + anyhow::bail!(TooBig) + } + + if number % 2 == 1 { + anyhow::bail!(NotEven) + } + + return Ok(StatusCode::OK); +} + +#[derive(Debug)] +struct TooBig; + +impl std::fmt::Display for TooBig { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "sorry, we can't handle big numbers like this") + } +} + +#[derive(Debug)] +struct NotEven; + +impl std::fmt::Display for NotEven { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "the given number is not even") + } +} + +async fn handle_rejection(err: Rejection) -> Result { + if let Some(anyhow) = err.find::() { + // here we can downcast the anyhow error to whatever we want + if let Some(_) = anyhow.downcast_ref::() { + return Ok(StatusCode::INTERNAL_SERVER_ERROR); + } + + if let Some(_) = anyhow.downcast_ref::() { + return Ok(StatusCode::BAD_REQUEST); + } + + return Ok(StatusCode::INTERNAL_SERVER_ERROR); + } + + Err(err) +} diff --git a/src/reject.rs b/src/reject.rs index 8fd28badd..9031ddc46 100644 --- a/src/reject.rs +++ b/src/reject.rs @@ -164,6 +164,16 @@ impl dyn Cause { } } +#[cfg(feature = "anyhow")] +impl Reject for anyhow::Error {} + +#[cfg(feature = "anyhow")] +impl From for Rejection { + fn from(anyhow: anyhow::Error) -> Self { + custom(anyhow) + } +} + pub(crate) fn known>(err: T) -> Rejection { Rejection::known(err.into()) } @@ -659,6 +669,46 @@ mod sealed { match self {} } } + + #[cfg(feature = "anyhow")] + impl CombineRejection for Infallible { + type One = Rejection; + type Combined = Rejection; + + fn combine(self, anyhow: anyhow::Error) -> Self::Combined { + crate::reject::custom(anyhow) + } + } + + #[cfg(feature = "anyhow")] + impl CombineRejection for anyhow::Error { + type One = Rejection; + type Combined = Rejection; + + fn combine(self, _: Infallible) -> Self::Combined { + crate::reject::custom(self) + } + } + + #[cfg(feature = "anyhow")] + impl CombineRejection for Rejection { + type One = Rejection; + type Combined = Rejection; + + fn combine(self, e: anyhow::Error) -> Self::Combined { + self.combine(crate::reject::custom(e)) + } + } + + #[cfg(feature = "anyhow")] + impl CombineRejection for anyhow::Error { + type One = Rejection; + type Combined = Rejection; + + fn combine(self, rejection: Rejection) -> Self::Combined { + rejection.combine(crate::reject::custom(self)) + } + } } #[cfg(test)]