Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PoC] [RFC] Allow routes to return anyhow::Error #588

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions Cargo.toml
Expand Up @@ -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"] }
Expand Down Expand Up @@ -83,3 +84,7 @@ required-features = ["websocket"]
[[example]]
name = "websockets_chat"
required-features = ["websocket"]

[[example]]
name = "anyhow"
required-features = ["anyhow"]
61 changes: 61 additions & 0 deletions 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<impl Reply> {
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<impl Reply, Rejection> {
if let Some(anyhow) = err.find::<anyhow::Error>() {
// here we can downcast the anyhow error to whatever we want
if let Some(_) = anyhow.downcast_ref::<TooBig>() {
return Ok(StatusCode::INTERNAL_SERVER_ERROR);
}

if let Some(_) = anyhow.downcast_ref::<NotEven>() {
return Ok(StatusCode::BAD_REQUEST);
}

return Ok(StatusCode::INTERNAL_SERVER_ERROR);
}

Err(err)
}
50 changes: 50 additions & 0 deletions src/reject.rs
Expand Up @@ -164,6 +164,16 @@ impl dyn Cause {
}
}

#[cfg(feature = "anyhow")]
impl Reject for anyhow::Error {}

#[cfg(feature = "anyhow")]
impl From<anyhow::Error> for Rejection {
fn from(anyhow: anyhow::Error) -> Self {
custom(anyhow)
}
}

pub(crate) fn known<T: Into<Known>>(err: T) -> Rejection {
Rejection::known(err.into())
}
Expand Down Expand Up @@ -659,6 +669,46 @@ mod sealed {
match self {}
}
}

#[cfg(feature = "anyhow")]
impl CombineRejection<anyhow::Error> 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<Infallible> for anyhow::Error {
type One = Rejection;
type Combined = Rejection;

fn combine(self, _: Infallible) -> Self::Combined {
crate::reject::custom(self)
}
}

#[cfg(feature = "anyhow")]
impl CombineRejection<anyhow::Error> 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<Rejection> for anyhow::Error {
type One = Rejection;
type Combined = Rejection;

fn combine(self, rejection: Rejection) -> Self::Combined {
rejection.combine(crate::reject::custom(self))
}
}
}

#[cfg(test)]
Expand Down