jsonrpc_sdk_macros/
lib.rs1#![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 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}