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}