1#![recursion_limit = "2048"]
2
3extern crate proc_macro;
4
5use proc_macro::TokenStream;
6use proc_macro2::Span;
7use proc_macro_hack::proc_macro_hack;
8use quote::quote;
9
10use syn::*;
11
12#[proc_macro_attribute]
13pub fn jsonrpc_method(attrs: TokenStream, item: TokenStream) -> TokenStream {
14 let method = parse_macro_input!(item as ImplItemMethod);
15
16 let attrs = parse_macro_input!(attrs as AttributeArgs);
17
18 let params = &method
19 .sig
20 .decl
21 .inputs
22 .iter()
23 .filter_map(|x| match *x {
24 FnArg::Captured(ref y) => Some(y),
25 _ => None
26 })
27 .filter_map(|x| match x.pat {
28 Pat::Ident(ref y) => Some(y),
29 _ => None
30 })
31 .map(|x| &x.ident)
32 .collect::<Vec<_>>();
33
34 let jsonrpc_callable_fn_ident = Ident::new(
35 &format!(
36 "jsonrpc_callable_for_{}",
37 method.sig.ident.to_string().trim_left_matches("r#").to_owned()
38 ),
39 Span::call_site()
40 );
41
42 let jsonrpc_method_name_ident = Ident::new(
43 &format!(
44 "JSONRPC_METHOD_NAME_FOR_{}",
45 method.sig.ident.to_string().trim_left_matches("r#").to_owned()
46 ),
47 Span::call_site()
48 );
49
50 let item_fn_ident = &method.sig.ident;
51
52 let mut item_fn_ident_s = item_fn_ident.to_string();
53
54 if let Some(rename) = attrs
55 .iter()
56 .filter_map(|x| match x {
57 NestedMeta::Meta(y) => Some(y),
58 _ => None
59 })
60 .filter_map(|x| match x {
61 Meta::NameValue(y) => Some(y),
62 _ => None
63 })
64 .find(|x| x.ident == "rename")
65 .and_then(|x| match x.lit {
66 Lit::Str(ref y) => Some(y),
67 _ => None
68 })
69 {
70 item_fn_ident_s = rename.value();
71 }
72
73 let mut rename_arg_mappings = attrs
74 .iter()
75 .filter_map(|x| match x {
76 NestedMeta::Meta(y) => Some(y),
77 _ => None
78 })
79 .filter_map(|x| match x {
80 Meta::List(y) => Some(y),
81 _ => None
82 })
83 .filter(|x| x.ident == "rename_args")
84 .flat_map(|x| x.nested.iter())
85 .filter_map(|x| match x {
86 NestedMeta::Meta(y) => Some(y),
87 _ => None
88 })
89 .filter_map(|x| match x {
90 Meta::NameValue(y) => Some(y),
91 _ => None
92 })
93 .filter_map(|x| match x.lit {
94 Lit::Str(ref y) => Some((x.ident.to_string(), y.value())),
95 _ => None
96 })
97 .collect::<std::collections::HashMap<String, String>>();
98
99 let param_names = ¶ms
100 .iter()
101 .map(|id| {
102 let name = id.to_string();
103 rename_arg_mappings.remove(&name).unwrap_or_else(|| name)
104 })
105 .collect::<Vec<_>>();
106
107 let extract_positional = extract_positional(param_names.len());
108 let extract_named = extract_named(param_names.len());
109
110 let jsonrpc_callable_fn: ImplItemMethod = {
111 if param_names.is_empty() {
112 parse_quote! {
113 pub fn #jsonrpc_callable_fn_ident(&self, params: Option<ezjsonrpc::exp::serde_json::Value>) -> Box<ezjsonrpc::exp::futures::Future<Item = ezjsonrpc::exp::serde_json::Value, Error = ezjsonrpc::Error> + Send> {
114 if params.as_ref()
115 .map(|x| x.as_object().map(|y| !y.is_empty()).unwrap_or(false) ||
116 x.as_array().map(|y| !y.is_empty()).unwrap_or(false) )
117 .unwrap_or(false) {
118 ezjsonrpc::utils::finish_callable(ezjsonrpc::exp::futures::future::err::<(),_>(ezjsonrpc::Error::invalid_params()))
119 } else {
120 ezjsonrpc::utils::finish_callable(self.#item_fn_ident())
121 }
122 }
123 }
124 } else {
125 parse_quote! {
126 pub fn #jsonrpc_callable_fn_ident(&self, params: Option<ezjsonrpc::exp::serde_json::Value>) -> Box<ezjsonrpc::exp::futures::Future<Item = ezjsonrpc::exp::serde_json::Value, Error = ezjsonrpc::Error> + Send> {
127 match params {
128 Some(ezjsonrpc::exp::serde_json::Value::Object(map)) => {
129 #extract_named
130 if let Ok((#(#params),*)) = extract(map, #(#param_names),*) {
131 return ezjsonrpc::utils::finish_callable(self.#item_fn_ident(#(#params),*));
132 }
133 },
134 Some(ezjsonrpc::exp::serde_json::Value::Array(vals)) => {
135 #extract_positional
136 if let Ok((#(#params),*)) = extract(vals) {
137 return ezjsonrpc::utils::finish_callable(self.#item_fn_ident(#(#params),*));
138 }
139 },
140 _ => {}
141 }
142 ezjsonrpc::utils::finish_callable(ezjsonrpc::exp::futures::future::err::<(),_>(ezjsonrpc::Error::invalid_params()))
143 }
144 }
145 }
146 };
147
148 let jsonrpc_method_name_fn: ImplItemConst = parse_quote! {
149 pub const #jsonrpc_method_name_ident: &'static str = #item_fn_ident_s;
150 };
151
152 let out = quote! {
153 #method
154 #jsonrpc_callable_fn
155 #jsonrpc_method_name_fn
156 };
157
158 out.into()
159}
160
161#[proc_macro_hack]
162pub fn methods(item: TokenStream) -> TokenStream {
163 use syn::parse::Parser;
164
165 let paths = <punctuated::Punctuated<Path, token::Comma>>::parse_terminated.parse(item).unwrap();
166
167 let parts = paths
168 .iter()
169 .map(|path| {
170 let mut json_callable_fn_path = path.clone();
171 {
172 let mut last_seg = json_callable_fn_path.segments.last_mut().unwrap();
173 last_seg.value_mut().ident = Ident::new(
174 &format!(
175 "jsonrpc_callable_for_{}",
176 last_seg.value().ident.to_string().trim_left_matches("r#").to_owned()
177 ),
178 Span::call_site()
179 );
180 }
181 let mut json_method_name_path = path.clone();
182 {
183 let mut last_seg = json_method_name_path.segments.last_mut().unwrap();
184 last_seg.value_mut().ident = Ident::new(
185 &format!(
186 "JSONRPC_METHOD_NAME_FOR_{}",
187 last_seg.value().ident.to_string().trim_left_matches("r#").to_owned()
188 ),
189 Span::call_site()
190 );
191 }
192 quote!(
193 #json_method_name_path => Some(#json_callable_fn_path),
194 )
195 })
196 .collect::<Vec<_>>();
197
198 let out = quote!(
199 |method: &str| {
200 match method {
201 #(#parts)*
202 _ => None
203 }
204 }
205 );
206
207 out.into()
208}
209
210fn extract_positional(up_to: usize) -> proc_macro2::TokenStream {
211 let tys =
212 (0..up_to).map(|i| Ident::new(&format!("T{}", i), Span::call_site())).collect::<Vec<_>>();
213 let gen = tys
214 .iter()
215 .map(|x| quote!(#x: ezjsonrpc::exp::serde::de::DeserializeOwned))
216 .collect::<Vec<_>>();
217
218 let ts =
219 (0..up_to).map(|i| Ident::new(&format!("t{}", i), Span::call_site())).collect::<Vec<_>>();
220
221 let mut ts_rev = ts.clone();
222 ts_rev.reverse();
223
224 let exprs = (0..up_to)
225 .map(|_| quote!(ezjsonrpc::exp::serde_json::from_value(vals.pop().unwrap())?))
226 .collect::<Vec<_>>();
227
228 quote! {
229 fn extract<#(#gen),*>(mut vals: Vec<ezjsonrpc::exp::serde_json::Value>) -> Result<(#(#tys),*), ezjsonrpc::exp::serde_json::Error> {
230 if vals.len() != #up_to {
231 return Err(ezjsonrpc::exp::serde::de::Error::custom("Invalid vec"));
232 }
233 let (#(#ts_rev),*) = (#(#exprs),*);
234 Ok((#(#ts),*))
235 }
236 }
237}
238
239fn extract_named(up_to: usize) -> proc_macro2::TokenStream {
240 let tys =
241 (0..up_to).map(|i| Ident::new(&format!("T{}", i), Span::call_site())).collect::<Vec<_>>();
242 let gen = tys
243 .iter()
244 .map(|x| quote!(#x: ezjsonrpc::exp::serde::de::DeserializeOwned))
245 .collect::<Vec<_>>();
246
247 let ts =
248 (0..up_to).map(|i| Ident::new(&format!("t{}", i), Span::call_site())).collect::<Vec<_>>();
249
250 let names =
251 (0..up_to).map(|i| Ident::new(&format!("n{}", i), Span::call_site())).collect::<Vec<_>>();
252
253 let names_and_tys = names.iter().map(|x| quote!(#x: &'static str)).collect::<Vec<_>>();
254
255 let mains = ts
256 .iter()
257 .zip(names.iter())
258 .map(|(t, n)| {
259 quote! {
260 let #t = if let Some(val) = map.remove(#n) {
261 ezjsonrpc::exp::serde_json::from_value(val)?
262 } else {
263 return Err(ezjsonrpc::exp::serde::de::Error::custom("Invalid map"));
264 };
265 }
266 })
267 .collect::<Vec<_>>();
268
269 quote! {
270 fn extract<#(#gen),*>(mut map: ezjsonrpc::exp::serde_json::Map<String, ezjsonrpc::exp::serde_json::Value>, #(#names_and_tys),*)
271 -> Result<(#(#tys),*), ezjsonrpc::exp::serde_json::Error> {
272 #(#mains)*
273 Ok((#(#ts),*))
274 }
275 }
276}