reset_router_macros/
lib.rs1extern crate proc_macro;
2extern crate proc_macro2;
3extern crate proc_macro_hack;
4extern crate quote;
5extern crate syn;
6
7use proc_macro::TokenStream;
8use proc_macro2::Span;
9use proc_macro_hack::proc_macro_hack;
10use quote::quote;
11
12use syn::*;
13
14#[proc_macro_hack]
15pub fn routes(item: TokenStream) -> TokenStream {
16 use syn::parse::Parser;
17
18 let paths = <punctuated::Punctuated<Path, token::Comma>>::parse_terminated.parse(item).unwrap();
19
20 let parts = paths
21 .iter()
22 .map(|path| {
23 let mut fn_name_path = path.clone();
24 {
25 let mut last_seg = fn_name_path.segments.last_mut().unwrap();
26 last_seg.value_mut().ident = Ident::new(
27 &format!(
28 "RESET_ROUTER_ROUTE_PARTS_FOR_{}",
29 last_seg.value().ident.to_string().trim_start_matches("r#").to_owned()
30 ),
31 Span::call_site(),
32 );
33 }
34 let fn_name_path = &fn_name_path;
35 quote!({
36 let (method, regex, priority) = #fn_name_path();
37 (method, regex, priority, #path.into())
38 })
39 })
40 .collect::<Vec<_>>();
41
42 let out = quote!(vec![#(#parts),*]);
43
44 out.into()
45}
46
47#[proc_macro_attribute]
51pub fn options(attrs: TokenStream, item: TokenStream) -> TokenStream {
52 named_route(attrs, item, "OPTIONS")
53}
54
55#[proc_macro_attribute]
59pub fn get(attrs: TokenStream, item: TokenStream) -> TokenStream {
60 named_route(attrs, item, "GET")
61}
62
63#[proc_macro_attribute]
67pub fn post(attrs: TokenStream, item: TokenStream) -> TokenStream {
68 named_route(attrs, item, "POST")
69}
70
71#[proc_macro_attribute]
75pub fn put(attrs: TokenStream, item: TokenStream) -> TokenStream {
76 named_route(attrs, item, "PUT")
77}
78
79#[proc_macro_attribute]
83pub fn delete(attrs: TokenStream, item: TokenStream) -> TokenStream {
84 named_route(attrs, item, "DELETE")
85}
86
87#[proc_macro_attribute]
91pub fn head(attrs: TokenStream, item: TokenStream) -> TokenStream {
92 named_route(attrs, item, "HEAD")
93}
94
95#[proc_macro_attribute]
99pub fn trace(attrs: TokenStream, item: TokenStream) -> TokenStream {
100 named_route(attrs, item, "TRACE")
101}
102
103#[proc_macro_attribute]
107pub fn connect(attrs: TokenStream, item: TokenStream) -> TokenStream {
108 named_route(attrs, item, "CONNECT")
109}
110
111#[proc_macro_attribute]
115pub fn patch(attrs: TokenStream, item: TokenStream) -> TokenStream {
116 named_route(attrs, item, "PATCH")
117}
118
119#[proc_macro_attribute]
125pub fn route(attrs: TokenStream, item: TokenStream) -> TokenStream {
126 let attrs = parse_macro_input!(attrs as AttributeArgs);
127 route_inner(attrs, item)
128}
129
130fn named_route(attrs: TokenStream, item: TokenStream, method: &str) -> TokenStream {
131 let attrs = parse_macro_input!(attrs as AttributeArgs);
132 let mut new_attrs = Vec::with_capacity(attrs.len());
133 for attr in attrs.into_iter() {
134 if let NestedMeta::Literal(ref lit) = attr {
135 new_attrs.push(NestedMeta::Meta(Meta::NameValue(parse_quote!(path=#lit))));
136 } else {
137 new_attrs.push(attr);
138 }
139 }
140 new_attrs.push(NestedMeta::Meta(Meta::NameValue(parse_quote!(methods=#method))));
141 route_inner(new_attrs, item)
142}
143
144fn route_inner(attrs: AttributeArgs, item: TokenStream) -> TokenStream {
145 let item = parse_macro_input!(item as ItemFn);
146
147 let params = attrs
148 .iter()
149 .filter_map(|x| match x {
150 NestedMeta::Meta(y) => Some(y),
151 _ => None,
152 })
153 .filter_map(|x| match x {
154 Meta::NameValue(y) => Some(y),
155 _ => None,
156 });
157
158 let regex_str = params
159 .clone()
160 .find(|x| x.ident == "path")
161 .and_then(|x| match x.lit {
162 Lit::Str(ref y) => Some(y),
163 _ => None,
164 })
165 .map(|x| x.value())
166 .expect("No path provided");
167
168 let methods_str = params
169 .clone()
170 .find(|x| x.ident == "methods")
171 .and_then(|x| match x.lit {
172 Lit::Str(ref y) => Some(y),
173 _ => None,
174 })
175 .map(|x| x.value())
176 .unwrap_or_else(|| "".into());
177
178 let priority_int = params
179 .clone()
180 .find(|x| x.ident == "priority")
181 .and_then(|x| match x.lit {
182 Lit::Int(ref y) => Some(y),
183 _ => None,
184 })
185 .map(|x| x.value() as u8)
186 .unwrap_or_else(|| 0);
187
188 let method_bits = {
189 let mut method_iter = methods_str.split(',').map(|x| x.trim().to_lowercase());
190
191 let cls = |s: &str| match s {
192 "options" => quote!(reset_router::bits::Method::OPTIONS),
193 "get" => quote!(reset_router::bits::Method::GET),
194 "post" => quote!(reset_router::bits::Method::POST),
195 "put" => quote!(reset_router::bits::Method::PUT),
196 "delete" => quote!(reset_router::bits::Method::DELETE),
197 "head" => quote!(reset_router::bits::Method::HEAD),
198 "trace" => quote!(reset_router::bits::Method::TRACE),
199 "connect" => quote!(reset_router::bits::Method::CONNECT),
200 "patch" => quote!(reset_router::bits::Method::PATCH),
201 _ => panic!("Unknown method parameter"),
202 };
203
204 let first = method_iter.next();
205
206 match first {
207 Some(m) => {
208 let base = cls(&m);
209 method_iter.fold(base, |acc, val| {
210 let val = cls(&val);
211 quote!(#acc | #val)
212 })
213 }
214 _ => quote!(reset_router::bits::Method::all()),
215 }
216 };
217
218 let fn_name = Ident::new(
219 &format!(
220 "RESET_ROUTER_ROUTE_PARTS_FOR_{}",
221 item.ident.to_string().trim_start_matches("r#").to_owned()
222 ),
223 Span::call_site(),
224 );
225
226 let out = quote! {
227 #item
228 pub fn #fn_name() -> (u32, &'static str, u8) {
229 ((#method_bits).bits(), #regex_str, #priority_int)
230 }
231 };
232
233 out.into()
234}