jsonrpc_v2_macros/
lib.rs

1#![recursion_limit = "2048"]
2
3extern crate proc_macro;
4
5use proc_macro::TokenStream;
6use proc_macro2::Span;
7use quote::quote;
8
9use syn::*;
10
11struct CustomOuterAttrs(Vec<Attribute>);
12
13impl parse::Parse for CustomOuterAttrs {
14    fn parse(input: parse::ParseStream) -> Result<Self> {
15        Ok(CustomOuterAttrs(input.call(Attribute::parse_outer)?))
16    }
17}
18
19#[proc_macro_attribute]
20pub fn jsonrpc_v2_method(attrs: TokenStream, item: TokenStream) -> TokenStream {
21    let method = parse_macro_input!(item as ItemFn);
22
23    let method_ident = &method.sig.ident;    
24
25    let attrs = parse_macro_input!(attrs as AttributeArgs);
26
27    let params = &method
28        .sig
29        .inputs
30        .iter()
31        .filter_map(|x| match *x {
32            FnArg::Typed(ref y) => Some(y),
33            _ => None
34        })
35        .filter_map(|x| match *x.pat {
36            Pat::Ident(ref y) => Some(y),
37            _ => None
38        })
39        .map(|x| &x.ident)
40        .collect::<Vec<_>>();
41
42    let mut wrapped_fn_ident = None;    
43    let wrapped_fn_path: Path = parse_quote!(wrapped_fn);
44    let externify_path: Path = parse_quote!(externify);
45
46    if let Some(wrapped_fn_name_lit) = attrs
47        .iter()
48        .filter_map(|x| match x {
49            NestedMeta::Meta(y) => Some(y),
50            _ => None
51        })
52        .filter_map(|x| match x {
53            Meta::NameValue(y) => Some(y),
54            _ => None
55        })
56        .find(|x| &x.path == &wrapped_fn_path)
57        .and_then(|x| match x.lit {
58            Lit::Str(ref y) => Some(y),
59            _ => None
60        })
61    {
62        wrapped_fn_ident = Some(Ident::new(&wrapped_fn_name_lit.value(), Span::call_site()));
63    }
64
65    let externify = attrs
66        .iter()
67        .filter_map(|x| match x {
68            NestedMeta::Meta(y) => Some(y),
69            _ => None
70        })
71        .filter_map(|x| match x {
72            Meta::NameValue(y) => Some(y),
73            _ => None
74        })
75        .find(|x| &x.path == &externify_path)
76        .map(|x| match x.lit {
77            Lit::Bool(ref y) => y.value,
78            _ => false
79        }).unwrap_or(false);
80
81    let mut method_as_outer = quote!();
82
83    let wrapped_fn = {
84
85        let ItemFn { sig: Signature { inputs, output, .. }, .. } = parse_quote! {
86            fn fn__(jsonrpc_v2::Params(params): jsonrpc_v2::Params<Option<jsonrpc_v2::exp::serde_json::Value>>) 
87                -> std::pin::Pin<Box<dyn std::future::Future<Output=Result<jsonrpc_v2::exp::serde_json::Value, jsonrpc_v2::Error>> + Send>> {}
88        };
89
90        let mut wrapped_fn = method.clone();
91        wrapped_fn.sig.asyncness = None;
92        wrapped_fn.sig.inputs = inputs;
93        wrapped_fn.sig.output = output;
94
95        if externify {
96
97            let mut no_mangle: CustomOuterAttrs = parse_quote!(#[no_mangle]);
98
99            wrapped_fn.attrs.append(&mut no_mangle.0);
100
101            wrapped_fn.vis = parse_quote!(pub);
102            wrapped_fn.sig.abi = Some(parse_quote!(extern));
103        }
104
105        let mut method_as_inner = quote!();
106        let mut method_call_ident = method_ident.clone();
107        
108        if let Some(wrapped_fn_ident) = wrapped_fn_ident {
109            wrapped_fn.sig.ident = wrapped_fn_ident;
110            method_as_outer = quote!(#method);
111        } else {
112            let jsonrpc_v2_inner_ident = Ident::new("jsonrpc_v2_inner", Span::call_site());
113            method_as_inner = {
114                let mut method = method.clone();
115                method.sig.ident = jsonrpc_v2_inner_ident.clone();
116                quote!(#method)
117            };
118            method_call_ident = jsonrpc_v2_inner_ident.clone();
119        }
120
121        let inner_call = quote!(#method_call_ident(#(#params),*).await?);
122
123        let block: Block = if params.is_empty() {
124            parse_quote!{{
125                #method_as_inner
126                Box::pin(async move {
127                    if params.as_ref()
128                        .map(|x| x.as_object().map(|y| !y.is_empty()).unwrap_or(false) ||
129                            x.as_array().map(|y| !y.is_empty()).unwrap_or(false) )
130                        .unwrap_or(false) {
131                        Err(jsonrpc_v2::Error::INVALID_PARAMS)
132                    } else {
133                        let res = #inner_call;
134                        let val = jsonrpc_v2::exp::serde_json::to_value(res)?;
135                        Ok(val)
136                    }
137                })
138            }}
139        } else {
140            let extract_positional = extract_positional(params.len());
141            let extract_named = extract_named(params.len());
142
143            let param_names = &params
144                .iter()
145                .map(|id| id.to_string() )
146                .collect::<Vec<_>>();
147
148            parse_quote!{{
149                #method_as_inner
150                
151                Box::pin(async move {
152                    match params {
153                        Some(jsonrpc_v2::exp::serde_json::Value::Object(map)) => {
154                            #extract_named
155                            if let Ok((#(#params),*)) = extract(map, #(#param_names),*) {
156                                let res = #inner_call;
157                                let val = jsonrpc_v2::exp::serde_json::to_value(res)?;
158                                return Ok(val);
159                            }
160                        },
161                        Some(jsonrpc_v2::exp::serde_json::Value::Array(vals)) => {
162                            #extract_positional
163                            if let Ok((#(#params),*)) = extract(vals) {
164                                let res = #inner_call;
165                                let val = jsonrpc_v2::exp::serde_json::to_value(res)?;
166                                return Ok(val);
167                            }
168                        },
169                        _ => {}
170                    }
171                    Err(jsonrpc_v2::Error::INVALID_PARAMS)
172                })
173            }}
174        };
175
176        wrapped_fn.block = Box::new(block);
177
178        wrapped_fn
179    };
180
181    let out = quote! {
182        #method_as_outer
183        #wrapped_fn
184    };
185
186    // panic!("{}", &out);
187
188    out.into()
189}
190
191fn extract_positional(up_to: usize) -> proc_macro2::TokenStream {
192    let tys =
193        (0..up_to).map(|i| Ident::new(&format!("T{}", i), Span::call_site())).collect::<Vec<_>>();
194    let gen = tys
195        .iter()
196        .map(|x| quote!(#x: jsonrpc_v2::exp::serde::de::DeserializeOwned))
197        .collect::<Vec<_>>();
198
199    let ts =
200        (0..up_to).map(|i| Ident::new(&format!("t{}", i), Span::call_site())).collect::<Vec<_>>();
201
202    let mut ts_rev = ts.clone();
203    ts_rev.reverse();
204
205    let exprs = (0..up_to)
206        .map(|_| quote!(jsonrpc_v2::exp::serde_json::from_value(vals.pop().unwrap()).map_err(|_| ())?))
207        .collect::<Vec<_>>();
208
209    quote! {
210        fn extract<#(#gen),*>(mut vals: Vec<jsonrpc_v2::exp::serde_json::Value>) -> Result<(#(#tys),*), ()> {
211            if vals.len() != #up_to {
212                return Err(());
213            }
214            let (#(#ts_rev),*) = (#(#exprs),*);
215            Ok((#(#ts),*))
216        }
217    }
218}
219
220fn extract_named(up_to: usize) -> proc_macro2::TokenStream {
221    let tys =
222        (0..up_to).map(|i| Ident::new(&format!("T{}", i), Span::call_site())).collect::<Vec<_>>();
223    let gen = tys
224        .iter()
225        .map(|x| quote!(#x: jsonrpc_v2::exp::serde::de::DeserializeOwned))
226        .collect::<Vec<_>>();
227
228    let ts =
229        (0..up_to).map(|i| Ident::new(&format!("t{}", i), Span::call_site())).collect::<Vec<_>>();
230
231    let names =
232        (0..up_to).map(|i| Ident::new(&format!("n{}", i), Span::call_site())).collect::<Vec<_>>();
233
234    let names_and_tys = names.iter().map(|x| quote!(#x: &'static str)).collect::<Vec<_>>();
235
236    let mains = ts
237        .iter()
238        .zip(names.iter())
239        .map(|(t, n)| {
240            quote! {
241                let #t = if let Some(val) = map.remove(#n) {
242                    jsonrpc_v2::exp::serde_json::from_value(val).map_err(|_| ())?
243                } else {
244                    return Err(());
245                };
246            }
247        })
248        .collect::<Vec<_>>();
249
250    quote! {
251        fn extract<#(#gen),*>(mut map: jsonrpc_v2::exp::serde_json::Map<String, jsonrpc_v2::exp::serde_json::Value>, #(#names_and_tys),*)
252            -> Result<(#(#tys),*), ()> {
253            #(#mains)*
254            Ok((#(#ts),*))
255        }
256    }
257}