webframework_derive/
lib.rs

1#![recursion_limit = "128"]
2extern crate proc_macro;
3extern crate syn;
4extern crate regex;
5extern crate lazy_static;
6
7use crate::proc_macro::TokenStream;
8use quote::{quote, quote_spanned};
9use syn::parse::{Parse, ParseStream, Result as SynResult};
10use syn::spanned::Spanned;
11use syn::{parse_macro_input, braced, ItemFn, Ident, Token, LitStr, Path, Visibility, Meta, Lit,
12FnArg, Pat, ArgCaptured, PatIdent, NestedMeta, AttributeArgs};
13use syn::token::Brace;
14use syn::punctuated::Punctuated;
15use regex::Regex;
16
17#[proc_macro_attribute]
18pub fn controller(args: TokenStream, input: TokenStream) -> TokenStream {
19    let func = parse_macro_input!(input as ItemFn);
20    let args = parse_macro_input!(args as AttributeArgs);
21
22    let ItemFn { vis, ident, block, decl, .. } = func;
23
24    let params: Vec<_> = args.iter()
25        .flat_map(|arg| if let NestedMeta::Meta(Meta::NameValue(values)) = arg { Some(values) } else { None })
26        .filter(|value| value.ident.to_string() == "params")
27        .flat_map(|value| {
28            if let Lit::Str(litstr) = &value.lit {
29                litstr.value().split(",").map(str::trim).map(str::to_string).collect::<Vec<_>>()
30            } else {
31                panic!("argument to params needs to be a string literal");
32            }
33        }).collect();
34
35
36    let inputs: Vec<_> = decl.inputs.iter().flat_map(|input| {
37        if let FnArg::Captured(ArgCaptured { pat: Pat::Ident(PatIdent { ident, .. }), ty, ..}) = input {
38            Some((ty, ident))
39        } else {
40            None
41        }
42    }).collect();
43
44    let input_tokens: Vec<_> = inputs.iter()
45        .filter(|(_ty, input)| params.iter().find(|&param| *param == input.to_string()).is_none())
46        .map(|(ty, input)| {
47            quote_spanned! {ty.span() =>
48                let #input: #ty = ::webframework_core::request::FromRequest::from_request(&req)?;
49            }
50        }).collect();
51
52    let param_tokens: Vec<_> = params.into_iter()
53        .map(|param| {
54            let (ty, ident) = inputs.iter().find(|&(_ty, ident)| {
55                ident.to_string() == param
56            }).expect(&format!("Param '{}' not found in arguments.", param));
57            quote! {
58                let param = match params.get(&String::from(#param)) {
59                    Some(p) => p,
60                    None => Err(::webframework_core::request::RequestErrorKind::ParamNotFound(String::from(#param)))?,
61                };
62                let #ident: #ty = ::webframework_core::request::FromParameter::from_parameter(param)?;
63            }
64        }).collect();;
65
66    TokenStream::from(quote!(
67        #[derive(Debug, Clone)]
68        #vis struct #ident;
69
70        impl ::webframework_core::router::Router for #ident {
71            fn handle(&self, mut req: ::webframework_core::request::Request,
72                      path: Option<String>, params: ::std::collections::HashMap<String, String>)
73                -> ::webframework_core::router::RouterResult {
74                if path.is_some() && path != Some("/".into()) {
75                    return ::webframework_core::router::RouterResult::Unhandled(req, params);
76                }
77                let result = || -> ::webframework_core::WebResult<_> {
78                    #(#input_tokens)*;
79                    #(#param_tokens)*;
80                    #block
81                };
82                ::webframework_core::router::RouterResult::Handled(
83                    Box::new(::futures::future::result(result().map(Into::into)))
84                )
85            }
86        }
87    ))
88}
89
90struct Routing {
91    visibility: Visibility,
92    name: Ident,
93    routes: Punctuated<Route, Token![;]>,
94}
95
96
97impl Parse for Routing {
98    fn parse(input: ParseStream) -> SynResult<Self> {
99        let visibility: Visibility = input.parse()?;
100        let name: Ident = input.parse()?;
101        input.parse::<Token![=>]>()?;
102        let content;
103        braced!(content in input);
104        let routes: Punctuated<Route, Token![;]> = content.parse_terminated(Route::parse)?;
105        Ok(Routing { visibility, name, routes })
106    }
107}
108
109struct InnerRoute {
110    restrictions: Vec<Ident>,
111    controller: Path,
112}
113
114enum InnerRouteKind {
115    Multiple(Punctuated<InnerRoute, Token![;]>),
116    Single(Path),
117    Meta(Ident, Path),
118}
119
120struct Route {
121    restrictions: Vec<Ident>,
122    path: Option<LitStr>,
123    kind: InnerRouteKind,
124}
125
126impl Parse for Route {
127    fn parse(input: ParseStream) -> SynResult<Self> {
128        if input.peek(Token![>>]) {
129            input.parse::<Token![>>]>()?;
130            let name: Ident = input.parse()?;
131            input.parse::<Token![=>]>()?;
132            let controller: Path = input.parse()?;
133            return Ok( Route { restrictions: vec![], path: None,
134                kind: InnerRouteKind::Meta(name, controller) } );
135        }
136
137        let mut restrictions: Vec<Ident> = vec![];
138        while input.peek(Ident) {
139            restrictions.push(input.parse()?);
140        }
141        let path: Option<LitStr> = input.parse().ok();
142        input.parse::<Token![=>]>()?;
143        if input.peek(Brace) {
144            let content;
145            braced!(content in input);
146            let inner_item = |input: ParseStream| {
147                let mut restrictions : Vec<Ident> = vec![];
148                while input.peek(Ident) {
149                    restrictions.push(input.parse()?);
150                }
151                if restrictions.is_empty() {
152                    return Err(input.error("you need to specify at least one filter"));
153                }
154                input.parse::<Token![=>]>()?;
155                let controller: Path = input.parse()?;
156                Ok(InnerRoute { restrictions, controller })
157            };
158
159            let inner_items: Punctuated<InnerRoute, Token![;]> = content.parse_terminated(inner_item)?;
160            return Ok(Route { restrictions, path, kind: InnerRouteKind::Multiple(inner_items) });
161        } else {
162            let inner_item: Path = input.parse()?;
163            return Ok(Route { restrictions, path, kind: InnerRouteKind::Single(inner_item) });
164        }
165    }
166}
167
168#[proc_macro]
169pub fn routing(input: TokenStream) -> TokenStream {
170    let Routing { visibility, name, routes } = parse_macro_input!(input as Routing);
171
172    let route_handlers: Vec<_> = routes.iter().map(|route| {
173        let restr = &route.restrictions;
174        let path = route.path.as_ref().map(|path| {
175
176            lazy_static::lazy_static! {
177                static ref items: Regex = Regex::new(":(?P<name>[a-zA-Z]+)").unwrap();
178            }
179
180            let path_value = path.value();
181            let capture_path = items.replace_all(&path_value, "(?P<$name>[^/]+)");
182
183            quote! {
184                {
185                    ::lazy_static::lazy_static! {
186                        static ref RE: ::webframework::request_filter::PathRegex = {
187                            let path = format!(r"\A({})(?:/|\z)", #capture_path);
188                            ::webframework::request_filter::PathRegex::from_regex(
189                                ::regex::Regex::new(&path).unwrap()
190                            )
191                        };
192                    };
193
194                    let matched_path = ::webframework_core::request_filter::PathFilter::handles(&*RE, &req, &path);
195
196                    match matched_path {
197                        ::webframework_core::request_filter::PathFilterResult::Matched(new_path, mut param) => {
198                            path = new_path;
199                            params.extend(param.drain());
200                            true
201                        }
202                        ::webframework_core::request_filter::PathFilterResult::NotMatched => false,
203                    }
204                }
205            }
206        }).unwrap_or_else(|| quote!{ true });
207        let handler = match &route.kind {
208            InnerRouteKind::Multiple(mul) => {
209                let inner = mul.iter().map(|inner| {
210                    let restr = &inner.restrictions;
211                    let ctrl = &inner.controller;
212                    let assert_router = quote_spanned! {ctrl.span() =>
213                        {
214                            fn __assert_router<F: ::webframework_core::router::Router>(_: F) { }
215                            __assert_router(#ctrl);
216                        }
217                    };
218                    quote! {
219                        if true #(&& ::webframework_core::request_filter::RequestFilter::handles(&#restr, &req))* {
220                            #assert_router
221
222                            match ::webframework_core::router::Router::handle(&#ctrl, req, Some(path.clone()), params) {
223                                ::webframework_core::router::RouterResult::Handled(resp) => {
224                                    return ::webframework_core::router::RouterResult::Handled(resp);
225                                }
226                                ::webframework_core::router::RouterResult::Unhandled(re, mut param) => {
227                                    req = re;
228                                    params = param;
229                                }
230                            }
231                        }
232                    }
233                });
234                quote! {
235                    #(#inner);*
236                }
237            }
238            InnerRouteKind::Single(sing) => {
239                let assert_router = quote_spanned! {sing.span() =>
240                    {
241                        fn __assert_router<F: ::webframework_core::router::Router>(_: F) { }
242                        __assert_router(#sing);
243                    }
244                };
245                quote! {
246                    #assert_router
247
248                    match ::webframework_core::router::Router::handle(&#sing, req, Some(path.clone()), params) {
249                        ::webframework_core::router::RouterResult::Handled(resp) => {
250                            return ::webframework_core::router::RouterResult::Handled(resp);
251                        }
252                        ::webframework_core::router::RouterResult::Unhandled(re, mut param) => {
253                            req = re;
254                            params = param;
255                        }
256                    }
257                }
258            }
259            InnerRouteKind::Meta(name, ctrl) => {
260                let assert_router = quote_spanned! {ctrl.span() =>
261                    {
262                        fn __assert_router<F: ::webframework_core::router::Router>(_: F) { }
263                        __assert_router(#ctrl);
264                    }
265                };
266
267                match &name.to_string()[..] {
268                    "NotFound" => {
269                        quote! {
270                            #assert_router
271
272                            match ::webframework_core::router::Router::handle(&#ctrl, req, None, params) {
273                                ::webframework_core::router::RouterResult::Handled(resp) => {
274                                    return ::webframework_core::router::RouterResult::Handled(resp);
275                                }
276                                ::webframework_core::router::RouterResult::Unhandled(re, mut param) => {
277                                    req = re;
278                                    params = param;
279                                }
280                            }
281                        }
282                    }
283                    _ => {
284                        panic!("Unknown meta element {}", name.to_string());
285                    }
286                }
287            }
288        };
289        quote! {
290            if true #(&& ::webframework_core::request_filter::RequestFilter::handles(&#restr, &req))* {
291                if #path {
292                    #handler;
293                }
294            }
295        }
296    }).collect();
297
298    let route_maps: Vec<_> = routes.iter().map(|_route| {
299        quote! {
300
301        }
302    }).collect();
303
304    let expanded = quote! {
305        #[derive(Debug, Clone)]
306        #visibility struct #name;
307
308        impl ::webframework_core::router::Router for #name {
309            fn handle(&self, mut req: ::webframework_core::request::Request,
310                      path: Option<String>, mut params: ::std::collections::HashMap<String, String>)
311                -> ::webframework_core::router::RouterResult {
312                let mut path = path.unwrap_or_else(|| req.uri().path().to_string());
313                #( #route_handlers );*;
314
315                return ::webframework_core::router::RouterResult::Unhandled(req, params);
316            }
317
318            fn router_map(&self) -> Option<::webframework_core::router::RouterMap> {
319                let mut map = ::webframework_core::router::RouterMap::new();
320
321                #( #route_maps );*
322
323                Some(map)
324            }
325        }
326    };
327
328    TokenStream::from(expanded)
329}