1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#![feature(proc_macro_span)]
#![feature(type_alias_impl_trait)]
#![feature(proc_macro_diagnostic)]
#![allow(unused_imports)]
#![allow(unused_variables)]
#![allow(unused_macros)]
#![allow(dead_code)]
#[macro_use]
extern crate syn;
#[macro_use]
extern crate quote;
use proc_macro::TokenStream;
use syn::parse::{Parse, ParseStream, Result};
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{parse_macro_input, token, Expr, Ident, Token, Type, Visibility};
fn create_route_impl(controller_impl: syn::ItemImpl) -> TokenStream {
let struct_name = match *controller_impl.clone().self_ty {
syn::Type::Path(type_path) => type_path.path.segments.first().unwrap().ident.to_string(),
_ => unimplemented!(),
};
struct Route {
path: TokenStream,
verb: String,
call: syn::Expr,
}
let routes: Vec<syn::export::TokenStream2> = controller_impl
.items
.iter()
.filter_map(|item| {
match item {
syn::ImplItem::Method(method) => {
let method_name = method.sig.ident.to_string();
let call: syn::Expr =
syn::parse_str(format!("{}::{}", struct_name, method_name).as_str())
.unwrap();
let attr = method.attrs.clone().into_iter().next().unwrap();
let path: syn::export::TokenStream2 = attr.tokens.clone().into();
let verb = attr.path.segments.first().unwrap().ident.to_string();
let get: syn::Expr = syn::parse_str("warp::get()").unwrap();
let ret = method.sig.output.clone();
let r = quote_spanned! {ret.span()=>
{
let this = this.clone();
warp::any()
.map(move || this.clone())
.and(warp::path!#path)
.and(#get)
.and_then(#call)
}
};
Some(r)
}
_ => None,
}
})
.collect();
let struct_ty: syn::TypePath = syn::parse_str(struct_name.as_str()).expect("struct name");
let impl_routes = quote! {
impl #struct_ty {
fn routes(self) -> impl warp::Filter<Extract=impl warp::Reply, Error=warp::Rejection> + Clone {
let dummy = warp::path!("thisshouldnevermatch")
.map(|| warp::reply());
let this = self;
dummy#( .or(#routes) )*
}
}
#controller_impl
};
impl_routes.into()
}
#[proc_macro_attribute]
pub fn routes(meta: TokenStream, input: TokenStream) -> TokenStream {
let item: syn::Item = syn::parse(input).expect("failed to parse input.. ");
match item {
syn::Item::Impl(impl_item) => create_route_impl(impl_item),
_ => unimplemented!(),
}
}
#[proc_macro_attribute]
pub fn get(_metadata: TokenStream, input: TokenStream) -> TokenStream {
let item: syn::Item = syn::parse(input).expect("failed to parse input");
match item {
syn::Item::Fn(func) => {
let output = quote! { #func };
output.into()
}
_ => {
unimplemented!()
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}