From edea3ab3069e13903de34ce00cd19d788b83f133 Mon Sep 17 00:00:00 2001 From: Kevin Butler Date: Fri, 17 Jul 2015 21:21:58 +0100 Subject: [PATCH] feat(macros): allow hinting the server data type in middleware macro This can sometimes be required when using some middleware which are predicated on the datatype of the Server. An alternative would be to litter the handler with extra type annotations (which is awkward without type ascription), or to use explicit type-annotated functions as middleware. Example usage: ``` middleware! { |res| // res is of type Response } ``` --- examples/session_example.rs | 2 +- src/macros/middleware.rs | 40 +++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/examples/session_example.rs b/examples/session_example.rs index 891c720323..c0b783cfcc 100644 --- a/examples/session_example.rs +++ b/examples/session_example.rs @@ -45,7 +45,7 @@ fn main() { (StatusCode::BadRequest, "Access denied.") }); - server.get("/secret", middleware! { |mut res| + server.get("/secret", middleware! { |mut res| match *res.session() { Some(ref user) if user == "foo" => (StatusCode::Ok, "Some hidden information!"), _ => (StatusCode::Forbidden, "Access denied.") diff --git a/src/macros/middleware.rs b/src/macros/middleware.rs index aaf924eeb3..8db1806e37 100644 --- a/src/macros/middleware.rs +++ b/src/macros/middleware.rs @@ -23,8 +23,27 @@ /// server.listen("127.0.0.1:6767"); /// # } /// ``` +/// +/// # Type hinting +/// Sometimes type inference is unable to determine the datatype for the server, +/// which can lead to a lot of extra type annotations. The `middleware!` macro +/// supports annotating the macro so as to drive the inference allowing the handler +/// code to remain with minimal annotations. +/// +/// ``` +/// # #[macro_use] extern crate nickel; +/// # fn main() { +/// # struct MyServerData; +/// middleware! { |_response| +/// // _response is of type Response +/// "Hello World" +/// } +/// # ; // This semicolon is required to satisfy returning `()` +/// # } +/// ``` #[macro_export] macro_rules! middleware { + (|mut $res:ident| <$data:path> $($b:tt)+) => { _middleware_inner!($res, mut $res, <$data> $($b)+) }; (|mut $res:ident| $($b:tt)+) => { _middleware_inner!($res, mut $res, $($b)+) }; (|$res:ident| $($b:tt)+) => { _middleware_inner!($res, $res, $($b)+) }; ($($b:tt)+) => { _middleware_inner!(_res, _res, $($b)+) }; @@ -33,6 +52,27 @@ macro_rules! middleware { #[doc(hidden)] #[macro_export] macro_rules! _middleware_inner { + ($res:ident, $res_binding:pat, <$data:path> $($b:tt)+) => {{ + use $crate::{MiddlewareResult,Responder, Response}; + + #[inline(always)] + fn restrict<'a, 'k, R: Responder<$data>>(r: R, res: Response<'a, 'k, $data>) + -> MiddlewareResult<'a, 'k, $data> { + res.send(r) + } + + // Inference fails due to thinking it's a (&Request, Response) with + // different mutability requirements + #[inline(always)] + fn restrict_closure(f: F) -> F + where F: for<'a, 'k> + Fn(Response<'a, 'k, $data>) + -> MiddlewareResult<'a, 'k, $data> + Send + Sync { f } + + restrict_closure(move |$res_binding| { + restrict(as_block!({$($b)+}), $res) + }) + }}; ($res:ident, $res_binding:pat, $($b:tt)+) => {{ use $crate::{MiddlewareResult,Responder, Response};