jsonrpc_sdk_macros/
lib.rs

1// Copyright (C) 2019 Boyu Yang
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9#![recursion_limit = "128"]
10
11extern crate proc_macro;
12
13use quote::quote;
14use syn::{parse::Error as ParseError, spanned::Spanned};
15
16fn snake_case(ident: &syn::Ident) -> syn::Ident {
17    let mut snake = String::new();
18    for (i, ch) in ident.to_string().char_indices() {
19        if i > 0 && ch.is_uppercase() {
20            snake.push('_');
21        }
22        snake.push(ch.to_ascii_lowercase());
23    }
24    syn::Ident::new(&snake, ident.span())
25}
26
27fn pascal_case(ident: &syn::Ident) -> syn::Ident {
28    let mut pascal = String::new();
29    let mut capitalize = true;
30    for ch in ident.to_string().chars() {
31        if ch == '_' {
32            capitalize = true;
33        } else if capitalize {
34            pascal.push(ch.to_ascii_uppercase());
35            capitalize = false;
36        } else {
37            pascal.push(ch);
38        }
39    }
40    syn::Ident::new(&pascal, ident.span())
41}
42
43struct JsonRpcApiDef {
44    pub(crate) name: syn::LitStr,
45    pub(crate) func: syn::Ident,
46    pub(crate) prefix: syn::Ident,
47    pub(crate) inputs: Vec<syn::Type>,
48    pub(crate) output: syn::Type,
49}
50
51struct JsonRpcClientDef {
52    pub(crate) name: syn::Ident,
53    pub(crate) vis: syn::Visibility,
54    pub(crate) apis: Vec<JsonRpcApiDef>,
55}
56
57impl syn::parse::Parse for JsonRpcClientDef {
58    fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
59        let item_trait = {
60            // ExprClosure
61            let _oror: syn::Token![||] = input.parse()?;
62            let content;
63            let _braces = syn::braced!(content in input);
64            content.parse()?
65        };
66        Self::parse_item_trait(item_trait)
67    }
68}
69
70impl JsonRpcClientDef {
71    fn parse_item_trait(it: syn::ItemTrait) -> syn::parse::Result<Self> {
72        if let Some(tk) = it.unsafety {
73            return Err(ParseError::new(tk.span, "don't support `unsafe`"));
74        }
75        if let Some(tk) = it.auto_token {
76            return Err(ParseError::new(tk.span, "don't support `auto`"));
77        }
78        if !it.generics.params.is_empty() {
79            let sp = it.generics.span();
80            return Err(ParseError::new(sp, "don't support generics"));
81        }
82        if !it.supertraits.is_empty() {
83            let sp = it.supertraits.span();
84            return Err(ParseError::new(sp, "don't support trait bound"));
85        }
86        let name = it.ident;
87        let vis = it.vis;
88        let mut apis = Vec::new();
89        for ti in it.items.into_iter() {
90            let api = Self::parse_trait_item(ti)?;
91            apis.push(api);
92        }
93        Ok(Self { name, vis, apis })
94    }
95
96    fn parse_trait_item(ti: syn::TraitItem) -> syn::parse::Result<JsonRpcApiDef> {
97        if let syn::TraitItem::Method(tim) = ti {
98            let syn::TraitItemMethod {
99                attrs,
100                sig,
101                default,
102                ..
103            } = tim;
104            if !attrs.is_empty() {
105                return Err(ParseError::new(attrs[0].span(), "don't support attributes"));
106            }
107            if default.is_some() {
108                return Err(ParseError::new(
109                    default.span(),
110                    "don't support default implementation",
111                ));
112            }
113            let syn::MethodSig {
114                constness,
115                unsafety,
116                asyncness,
117                abi,
118                ident,
119                decl,
120            } = sig;
121            if let Some(tk) = constness {
122                return Err(ParseError::new(tk.span, "don't support `const`"));
123            }
124            if let Some(tk) = unsafety {
125                return Err(ParseError::new(tk.span, "don't support `unsafe`"));
126            }
127            if let Some(tk) = asyncness {
128                return Err(ParseError::new(tk.span, "don't support `async`"));
129            }
130            if let Some(tk) = abi {
131                return Err(ParseError::new(
132                    tk.span(),
133                    "don't support  binary interface",
134                ));
135            }
136            if !decl.generics.params.is_empty() {
137                let sp = decl.generics.span();
138                return Err(ParseError::new(sp, "don't support generics"));
139            }
140            if let Some(ref tk) = decl.variadic {
141                return Err(ParseError::new(tk.span(), "don't support variadic"));
142            }
143            let name = syn::LitStr::new(&ident.to_string(), ident.span());
144            let func = snake_case(&ident);
145            let prefix = pascal_case(&ident);
146            let mut inputs = Vec::new();
147            for input in decl.inputs.into_iter() {
148                if let syn::FnArg::Ignored(ty) = input {
149                    inputs.push(ty);
150                } else {
151                    return Err(ParseError::new(
152                        input.span(),
153                        "only support types not bound to any pattern",
154                    ));
155                }
156            }
157            let output = if let syn::ReturnType::Type(_, bt) = decl.output {
158                *bt
159            } else {
160                syn::Type::Tuple(syn::TypeTuple {
161                    paren_token: syn::token::Paren::default(),
162                    elems: syn::punctuated::Punctuated::new(),
163                })
164            };
165            let api = JsonRpcApiDef {
166                name,
167                func,
168                prefix,
169                inputs,
170                output,
171            };
172            Ok(api)
173        } else {
174            Err(ParseError::new(ti.span(), "only support methods"))
175        }
176    }
177}
178
179fn construct_idents(ident: &syn::Ident) -> (syn::Ident, syn::Ident) {
180    let ident_str = ident.to_string();
181    let request_str = ident_str.clone() + "JsonRpcRequest";
182    let request = syn::Ident::new(&request_str, proc_macro2::Span::call_site());
183    let response_str = ident_str + "JsonRpcResponse";
184    let response = syn::Ident::new(&response_str, proc_macro2::Span::call_site());
185    (request, response)
186}
187
188fn construct_jsonrpc_api(
189    api: JsonRpcApiDef,
190) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
191    let (request, response) = construct_idents(&api.prefix);
192    let common_part = construct_jsonrpc_common_part(&api, &request, &response);
193    let JsonRpcApiDef { func, inputs, .. } = api;
194    if inputs.is_empty() {
195        let define_part = quote!(
196            #[derive(Debug)]
197            pub struct #request {}
198            impl ::std::convert::TryFrom<#request> for jsonrpc_core::Params {
199                type Error = serde_json::Error;
200                fn try_from(_req: #request) -> serde_json::Result<Self> {
201                    Ok(jsonrpc_core::Params::None)
202                }
203            }
204            #common_part
205        );
206        let impl_part = quote!(
207            pub fn #func() -> #request {
208                #request {}
209            }
210        );
211        (define_part, impl_part)
212    } else {
213        let inputs_len = inputs.len() as u64;
214        let len = syn::LitInt::new(
215            inputs_len,
216            syn::IntSuffix::None,
217            proc_macro2::Span::call_site(),
218        );
219        let idx = &(0..inputs_len)
220            .map(|i| syn::LitInt::new(i, syn::IntSuffix::None, proc_macro2::Span::call_site()))
221            .collect::<Vec<_>>();
222        let arg = (0..inputs_len)
223            .map(|i| format!("v{}", i))
224            .map(|ref i| syn::Ident::new(i, proc_macro2::Span::call_site()))
225            .collect::<Vec<_>>();
226        let arg1 = &arg;
227        let arg2 = &arg;
228        let input = &inputs;
229        let define_part = quote!(
230            #[derive(Debug)]
231            pub struct #request (#(#input,)*);
232            impl ::std::convert::TryFrom<#request> for jsonrpc_core::Params {
233                type Error = serde_json::Error;
234                fn try_from(req: #request) -> serde_json::Result<Self> {
235                    let mut values = Vec::with_capacity(#len);
236                    #(values.push(serde_json::to_value(req.#idx)?);)*
237                    Ok(jsonrpc_core::Params::Array(values))
238                }
239            }
240            #common_part
241        );
242        let impl_part = quote!(
243            pub fn #func(#(#arg1: #input,)*) -> #request {
244                #request (#(#arg2,)*)
245            }
246        );
247        (define_part, impl_part)
248    }
249}
250
251fn construct_jsonrpc_common_part(
252    api: &JsonRpcApiDef,
253    request: &syn::Ident,
254    response: &syn::Ident,
255) -> proc_macro2::TokenStream {
256    let JsonRpcApiDef {
257        ref name,
258        ref output,
259        ..
260    } = api;
261    quote!(
262        pub struct #response(#output);
263
264        impl ::std::convert::From<#response> for #output {
265            fn from(r: #response) -> #output {
266                r.0
267            }
268        }
269
270        impl ::std::convert::From<#output> for #response {
271            fn from(o: #output) -> #response {
272                #response(o)
273            }
274        }
275
276        impl ::std::convert::TryFrom<serde_json::Value> for #response {
277            type Error = serde_json::Error;
278            fn try_from(val: serde_json::Value) -> serde_json::Result<Self> {
279                serde_json::from_value(val).map(#response)
280            }
281        }
282
283        impl JsonRpcRequest for #request {
284            type Output = #response;
285
286            #[inline]
287            fn method() -> &'static str {
288                #name
289            }
290        }
291    )
292}
293
294#[proc_macro]
295pub fn jsonrpc_interfaces(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
296    let inputs = syn::parse_macro_input!(input as JsonRpcClientDef);
297    let expanded = {
298        let JsonRpcClientDef { name, vis, apis } = inputs;
299        let mut defs = quote!();
300        let mut impls = quote!();
301        for api in apis.into_iter() {
302            let (define_part, impl_part) = construct_jsonrpc_api(api);
303            defs = quote!(#defs #define_part);
304            impls = quote!(#impls #impl_part);
305        }
306        quote!(
307            #defs
308            #vis struct #name {}
309            impl #name {
310                #impls
311            }
312        )
313    };
314    expanded.into()
315}