eth_jsonrpc_types_internals/
lib.rs

1// Copyright Rivtower Technologies LLC.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15extern crate proc_macro;
16extern crate proc_macro2;
17#[macro_use]
18extern crate syn;
19#[macro_use]
20extern crate quote;
21
22use syn::parse::{Parse, ParseStream};
23use syn::punctuated::Punctuated;
24use syn::token;
25
26struct TypeWithAttrs {
27    attrs: Vec<syn::Attribute>,
28    typ: syn::Type,
29}
30
31impl Parse for TypeWithAttrs {
32    fn parse(input: ParseStream) -> syn::Result<Self> {
33        Ok(Self {
34            attrs: input.call(syn::Attribute::parse_outer)?,
35            typ: input.parse()?,
36        })
37    }
38}
39
40struct ParamsType {
41    name: syn::Ident,
42    colon_token: Token![:],
43    brace_token: token::Bracket,
44    fields: Punctuated<TypeWithAttrs, Token![,]>,
45    comma_token: Token![,],
46    resp: syn::Ident,
47}
48
49impl Parse for ParamsType {
50    fn parse(input: ParseStream) -> syn::Result<Self> {
51        let content;
52        Ok(ParamsType {
53            name: input.parse()?,
54            colon_token: input.parse()?,
55            brace_token: bracketed!(content in input),
56            fields: content.parse_terminated(TypeWithAttrs::parse)?,
57            comma_token: input.parse()?,
58            resp: input.parse()?,
59        })
60    }
61}
62
63// Get JSON-RPC name from params name.
64// The params name should be `format!("{}Params", capitalize_first(method_name))`.
65fn construct_rpcname_from_params_name(params_name: &str) -> syn::LitStr {
66    if params_name.len() <= 6 {
67        panic!("The name of params [{}] is too short.", params_name);
68    }
69    if !params_name.ends_with("Params") {
70        panic!("Please named the params as: method_name + \"Params\".");
71    }
72    let rpcname = params_name[..1].to_ascii_lowercase() + &params_name[1..params_name.len() - 6];
73    syn::LitStr::new(&rpcname, proc_macro2::Span::call_site())
74}
75
76#[proc_macro]
77pub fn construct_rpcname(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
78    let input: proc_macro2::TokenStream = input.into();
79    let output = {
80        let params_name: syn::Ident = syn::parse2(input).unwrap();
81        let rpcname = construct_rpcname_from_params_name(params_name.to_string().as_ref());
82        quote!(#rpcname)
83    };
84    output.into()
85}
86
87fn generate_attrs_list(attrs_vec: &[syn::Attribute]) -> proc_macro2::TokenStream {
88    let mut attrs = quote!();
89    for attr in attrs_vec.iter() {
90        attrs = quote!(#attrs #attr);
91    }
92    quote!(#attrs)
93}
94
95#[proc_macro]
96pub fn construct_params(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
97    let input = proc_macro2::TokenStream::from(input);
98
99    let output = {
100        let ParamsType {
101            name,
102            colon_token: _colon_token,
103            brace_token: _brace_token,
104            fields,
105            comma_token: _comma_token,
106            resp,
107        } = syn::parse2(input).unwrap();
108
109        let fields_size = fields.len();
110        let mut ele_req = fields_size;
111        let rpcname = construct_rpcname_from_params_name(name.to_string().as_ref());
112
113        let mut types = quote!();
114        let mut params_with_types = quote!();
115        let mut params = quote!();
116        let mut params_into_vec = quote!();
117
118        match fields_size {
119            0 => {}
120            1 => {
121                let TypeWithAttrs { attrs, typ } = &fields.iter().next().unwrap();
122                if !attrs.is_empty() {
123                    ele_req = ele_req - 1;
124                }
125                let param_attrs = generate_attrs_list(attrs);
126                types = quote!(#param_attrs pub #typ, #[serde(skip)] OneItemTupleTrick);
127                params_with_types = quote!(param: #typ);
128                params = quote!(param, OneItemTupleTrick::default());
129                let index = syn::Index::from(0);
130                params_into_vec = quote!(serde_json::to_value(self.#index).unwrap())
131            }
132            _ => {
133                let mut param_num = 0;
134                for TypeWithAttrs { attrs, typ } in fields.iter() {
135                    let param_attrs = generate_attrs_list(attrs.as_slice());
136                    if !attrs.is_empty() {
137                        ele_req = ele_req - 1;
138                    }
139                    let param_name = format!("p{}", param_num);
140                    let param_name = syn::Ident::new(&param_name, proc_macro2::Span::call_site());
141                    types = quote!(#types #param_attrs pub #typ,);
142                    params_with_types = quote!(#params_with_types #param_name: #typ,);
143                    params = quote!(#params #param_name,);
144                    let index = syn::Index::from(param_num);
145                    params_into_vec = quote!(
146                        #params_into_vec
147                        serde_json::to_value(self.#index).unwrap(), );
148                    param_num += 1;
149                }
150            }
151        };
152
153        quote!(
154            #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
155            pub struct #name (#types);
156
157            impl #name {
158                pub fn new(#params_with_types) -> #name {
159                    #name(#params)
160                }
161            }
162
163            impl JsonRpcRequest for #name {
164
165                type Response = #resp;
166
167                fn required_len() -> usize {
168                    #ele_req
169                }
170
171                fn valid_len() -> usize {
172                    #fields_size
173                }
174
175                fn method_name(&self) -> &'static str {
176                    #rpcname
177                }
178
179                fn value_vec(self) -> Vec<serde_json::Value> {
180                    vec![#params_into_vec]
181                }
182            }
183        )
184    };
185
186    proc_macro::TokenStream::from(output)
187}