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(|¶m| *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}