diff --git a/examples/dyn_reply.rs b/examples/dyn_reply.rs new file mode 100644 index 000000000..76db5ea7b --- /dev/null +++ b/examples/dyn_reply.rs @@ -0,0 +1,18 @@ +#![deny(warnings)] +use warp::{http::StatusCode, Filter}; + +async fn dyn_reply(word: String) -> Result, warp::Rejection> { + if &word == "hello" { + // a cast is needed for now, see https://github.com/rust-lang/rust/issues/60424 + Ok(Box::new("world") as Box) + } else { + Ok(Box::new(StatusCode::BAD_REQUEST) as Box) + } +} + +#[tokio::main] +async fn main() { + let routes = warp::path::param().and_then(dyn_reply); + + warp::serve(routes).run(([127, 0, 0, 1], 3030)).await; +} diff --git a/src/reply.rs b/src/reply.rs index d1afc2227..c9593ff31 100644 --- a/src/reply.rs +++ b/src/reply.rs @@ -47,6 +47,7 @@ use serde_json; use crate::reject::IsReject; // This re-export just looks weird in docs... pub(crate) use self::sealed::Reply_; +use self::sealed::{BoxedReply, Internal}; #[doc(hidden)] pub use crate::filters::reply as with; @@ -226,8 +227,7 @@ where /// /// let route = warp::any().map(handler); /// ``` -//NOTE: This list is duplicated in the module documentation. -pub trait Reply: Send { +pub trait Reply: BoxedReply + Send { /// Converts the given value into a [`Response`]. /// /// [`Response`]: type.Response.html @@ -292,6 +292,12 @@ pub trait Reply: Send { */ } +impl Reply for Box { + fn into_response(self) -> Response { + self.boxed_into_response(Internal) + } +} + fn _assert_object_safe() { fn _assert(_: &dyn Reply) {} } @@ -540,6 +546,23 @@ mod sealed { self.0 } } + + #[allow(missing_debug_implementations)] + pub struct Internal; + + // Implemented for all types that implement `Reply`. + // + // A user doesn't need to worry about this, it's just trait + // hackery to get `Box` working. + pub trait BoxedReply { + fn boxed_into_response(self: Box, internal: Internal) -> Response; + } + + impl BoxedReply for T { + fn boxed_into_response(self: Box, _: Internal) -> Response { + self.into_response() + } + } } #[cfg(test)] @@ -568,4 +591,10 @@ mod tests { assert_eq!(res.status(), 500); } + #[test] + fn boxed_reply() { + let r: Box = Box::new(reply()); + let resp = r.into_response(); + assert_eq!(resp.status(), 200); + } }