iron_middlefiddle/
lib.rs

1//! There are cases where you may only want specific routes within
2//! a [`Router`](https://docs.rs/router/0.5.1/router/struct.Router.html) to use some piece of
3//! middleware (e.g. routes that require the user to be logged in). Adding route-specific
4//! middleware in Iron is a repetitive and messy business which is where `iron_middlefiddle` comes
5//! in. It provides a convenience macro for adding one or more
6//! [`BeforeMiddleware`](https://docs.rs/iron/0.5.1/iron/middleware/trait.BeforeMiddleware.html) or
7//! [`AfterMiddleware`](https://docs.rs/iron/0.5.1/iron/middleware/trait.AfterMiddleware.html)s to
8//! multiple routes at once.
9//!
10//! ## Example usage
11//!
12//! ```rust,no_run
13//! #[macro_use]
14//! extern crate iron_middlefiddle;
15//! extern crate iron;
16//! extern crate mount;
17//! extern crate router;
18//!
19//! use iron_middlefiddle::Middleware;
20//! use mount::Mount;
21//! use router::Router;
22//!
23//! mod controllers;
24//! mod middleware;
25//!
26//! fn main() {
27//!     let mut frontend_router = Router::new();
28//!
29//!     // Add some `frontend_router` routes and the middleware they should use:
30//!
31//!     middlefiddle! {
32//!         router => frontend_router,
33//!         routes => {
34//!             lorem: get "/lorem" => controllers::lorem::index,
35//!             ipsum: get "/ipsum" => controllers::ipsum::index,
36//!             dolor: get "/dolor" => controllers::dolor::index,
37//!         },
38//!         middleware => {
39//!             Middleware::BeforeMiddleware => middleware::auth::TokenValidity,
40//!         },
41//!     };
42//!
43//!     // Add some more `frontend_router` routes that aren't going to need the middleware:
44//!
45//!     frontend_router.get("/amet", controllers::amet::index, "amet");
46//!
47//!     // The usual…
48//!
49//!     let mut mount = Mount::new();
50//!     mount.mount("/", frontend_router)
51//!
52//!     Iron::new(mount).http("127.0.0.1:8888").unwrap();
53//! }
54//! ```
55
56extern crate iron;
57
58use iron::{Handler, IronResult, Request, Response, Chain};
59
60/// Specifies the type of middleware you are passing to the routes.
61///
62/// ```rust,no_run
63/// middlefiddle! {
64///     router => some_router,
65///     routes => {
66///         // Some routes…
67///     },
68///     middleware => {
69///         Middleware::BeforeMiddleware => middleware::SomeMiddleware,
70///         Middleware::BeforeMiddleware => middleware::SomeOtherMiddleware,
71///         Middleware::AfterMiddleware => middleware::SomeMoreMiddleware,
72///     },
73/// };
74/// ```
75
76pub enum Middleware {
77    /// ```rust,no_run
78    /// middlefiddle! {
79    ///     router => some_router,
80    ///     routes => {
81    ///         // Some routes…
82    ///     },
83    ///     middleware => {
84    ///         Middleware::BeforeMiddleware => middleware::SomeMiddleware,
85    ///     },
86    /// };
87    /// ```
88
89    BeforeMiddleware(Box<iron::BeforeMiddleware>),
90
91    /// ```rust,no_run
92    /// middlefiddle! {
93    ///     router => some_router,
94    ///     routes => {
95    ///         // Some routes…
96    ///     },
97    ///     middleware => {
98    ///         Middleware::AfterMiddleware => middleware::SomeMiddleware,
99    ///     },
100    /// };
101    /// ```
102
103    AfterMiddleware(Box<iron::AfterMiddleware>),
104}
105
106#[doc(hidden)]
107pub struct Middlefiddle {
108    chain: Chain,
109}
110
111#[doc(hidden)]
112pub struct Route {
113    pub id: Option<String>,
114    pub method: String,
115    pub route: Option<String>,
116    pub handler: Option<Box<Handler>>,
117}
118
119impl Middlefiddle {
120    pub fn new<H: Handler>(handler: H, middleware: Vec<Box<Middleware>>) -> Self {
121        let mut chain = Chain::new(handler);
122
123        for m in middleware.into_iter() {
124            match *m {
125                Middleware::BeforeMiddleware(i) => {
126                    chain.link_before(i);
127                },
128                Middleware::AfterMiddleware(i) => {
129                    chain.link_after(i);
130                }
131            }
132        }
133
134        Middlefiddle {
135            chain: chain
136        }
137    }
138}
139
140impl Handler for Middlefiddle {
141    fn handle(&self, req: &mut Request) -> IronResult<Response> {
142        self.chain.handle(req)
143    }
144}
145
146/// The main `iron_middlefiddle` macro.
147///
148///
149/// ```rust,no_run
150/// middlefiddle! {
151///     // Pass the router you want the routes to be added to to `router`:
152///     router => some_router,
153///
154///     // `routes` takes one or more routes. Each one of them will have every
155///     // piece of middleware in `middleware` applied to them:
156///     routes => {
157///         // An example route that follows the formatting
158///         // `route_id: method "/an/example/path" => controller`:
159///         lorem: get "/lorem" => controllers::lorem::index,
160///
161///         // There can be as many of these as you like…
162///     },
163///
164///     // `middleware` takes all of the middlewares you want to apply to each
165///     // of the routes specified above.
166///     middleware => {
167///         // An example `BeforeMiddleware`:
168///         Middleware::BeforeMiddleware => middleware::SomeBeforeMiddleware,
169///
170///         // An example `AfterMiddleware`:
171///         Middleware::AfterMiddleware => middleware::SomeAfterMiddleware,
172///
173///         // There can be as many of these as you like…
174///     },
175/// };
176/// ```
177///
178/// ## Notes
179///
180/// - The formatting of the contents of `routes => { ... }` intentionally matches that of the router
181/// crate's own [router macro](https://docs.rs/router/0.5.1/router/macro.router.html) in an effort
182/// to make any potential refactoring easier.
183///
184/// - `$route_method` supports the following methods (must be lowercase):
185///     - `get`
186///     - `post`
187///     - `put`
188///     - `delete`
189///     - `head`
190///     - `patch`
191///     - `options`
192///     - `any`
193
194#[macro_export]
195macro_rules! middlefiddle {
196    (
197        router => $router:expr,
198        routes => {
199            $(
200                $route_id:ident: $route_method:ident $route:expr => $route_handler:expr
201            ),+
202            $(,)*
203        },
204        middleware => {
205            $(
206                $middleware_type:expr => $middleware_handler:expr
207            ),*
208            $(,)*
209        }
210        $(,)*
211    ) => ({
212        use iron_middlefiddle::{Middlefiddle, Route};
213
214        let mut routes = Vec::new();
215
216        $(
217            routes.push(Route {
218                id: Some(stringify!($route_id).to_string()),
219                method: stringify!($route_method).to_string(),
220                route: Some($route.to_string()),
221                handler: Some(Box::new($route_handler)),
222            });
223        )*
224
225        for mut route in routes {
226            let mut middleware = Vec::new();
227
228            $(
229                middleware.push(Box::new($middleware_type(Box::new($middleware_handler))));
230            )*
231
232            let route_id = route.id.take();
233            let route_route = route.route.take();
234            let route_handler = route.handler.take();
235            let middleware_chain = Middlefiddle::new(route_handler.unwrap(), middleware);
236
237            match route.method.as_ref() {
238                "get" => { $router.get(route_route.unwrap().to_string(), middleware_chain, route_id.unwrap().to_string()); },
239                "post" => { $router.post(route_route.unwrap().to_string(), middleware_chain, route_id.unwrap().to_string()); },
240                "put" => { $router.put(route_route.unwrap().to_string(), middleware_chain, route_id.unwrap().to_string()); },
241                "delete" => { $router.delete(route_route.unwrap().to_string(), middleware_chain, route_id.unwrap().to_string()); },
242                "head" => { $router.head(route_route.unwrap().to_string(), middleware_chain, route_id.unwrap().to_string()); },
243                "patch" => { $router.patch(route_route.unwrap().to_string(), middleware_chain, route_id.unwrap().to_string()); },
244                "options" => { $router.options(route_route.unwrap().to_string(), middleware_chain, route_id.unwrap().to_string()); },
245                "any" => { $router.any(route_route.unwrap().to_string(), middleware_chain, route_id.unwrap().to_string()); },
246                _ => {}
247            }
248        }
249    });
250}