easy_jsonrpc_proc_macro_mw/
lib.rs1#![recursion_limit = "256"]
5
6extern crate proc_macro;
7use heck::SnakeCase;
8use proc_macro2::{self, Span, TokenStream};
9use quote::{quote, quote_spanned};
10use syn::{
11 parse_macro_input, punctuated::Punctuated, spanned::Spanned, token::Paren, ArgSelfRef, FnArg,
12 FnDecl, Ident, ItemTrait, MethodSig, Pat, PatIdent, ReturnType, TraitItem, Type, TypeTuple,
13};
14
15#[proc_macro_attribute]
47pub fn rpc(_: proc_macro::TokenStream, item: proc_macro::TokenStream) -> proc_macro::TokenStream {
48 let trait_def = parse_macro_input!(item as ItemTrait);
49 let server_impl = raise_if_err(impl_server(&trait_def));
50 let client_impl = raise_if_err(impl_client(&trait_def));
51 proc_macro::TokenStream::from(quote! {
52 #trait_def
53 #server_impl
54 #client_impl
55 })
56}
57
58fn raise_if_err(res: Result<TokenStream, Rejections>) -> TokenStream {
60 match res {
61 Ok(stream) => stream,
62 Err(rej) => rej.raise(),
63 }
64}
65
66fn impl_server(tr: &ItemTrait) -> Result<TokenStream, Rejections> {
68 let trait_name = &tr.ident;
69 let methods: Vec<&MethodSig> = trait_methods(&tr)?;
70
71 let handlers = methods.iter().map(|method| {
72 let method_literal = method.ident.to_string();
73 let method_return_type_span = return_type_span(&method);
74 let handler = add_handler(trait_name, method)?;
75 let try_serialize = quote_spanned! {
76 method_return_type_span =>
77 easy_jsonrpc_mw::try_serialize(&result)
78 };
79 Ok(quote! { #method_literal => {
80 let result = #handler;
81 #try_serialize
82 }})
83 });
84 let handlers: Vec<TokenStream> = partition(handlers)?;
85
86 Ok(quote! {
87 impl easy_jsonrpc_mw::Handler for dyn #trait_name {
88 fn handle(&self, method: &str, params: easy_jsonrpc_mw::Params)
89 -> Result<easy_jsonrpc_mw::Value, easy_jsonrpc_mw::Error> {
90 match method {
91 #(#handlers,)*
92 _ => Err(easy_jsonrpc_mw::Error::method_not_found()),
93 }
94 }
95 }
96 })
97}
98
99fn impl_client(tr: &ItemTrait) -> Result<TokenStream, Rejections> {
100 let trait_name = &tr.ident;
101 let methods: Vec<&MethodSig> = trait_methods(&tr)?;
102 let mod_name = Ident::new(&trait_name.to_string().to_snake_case(), Span::call_site());
103 let method_impls = methods
104 .iter()
105 .map(|method| impl_client_method(*method))
106 .collect::<Result<Vec<TokenStream>, Rejections>>()?;
107
108 Ok(quote! {
109 pub enum #mod_name {}
113 impl #mod_name {
114 #(#method_impls)*
115 }
116 })
117}
118
119fn impl_client_method(method: &MethodSig) -> Result<TokenStream, Rejections> {
120 let method_name = &method.ident;
121 let method_name_literal = &method_name.to_string();
122 let args = get_args(&method.decl)?;
123 let fn_definition_args: &Vec<_> = &args
124 .iter()
125 .enumerate()
126 .map(|(i, (name, typ))| {
127 let arg_num_name = Ident::new(&format!("arg{}", i), name.span());
128 quote! {#arg_num_name: #typ}
129 })
130 .collect();
131 let args_serialize: &Vec<_> = &args
132 .iter()
133 .enumerate()
134 .map(|(i, (name, _))| {
135 let arg_num_name = Ident::new(&format!("arg{}", i), name.span());
136 quote! {
137 easy_jsonrpc_mw::serde_json::to_value(#arg_num_name).map_err(|_| easy_jsonrpc_mw::ArgSerializeError)?
138 }
139 })
140 .collect();
141 let return_typ = return_type(&method);
142
143 Ok(quote! {
144 pub fn #method_name ( #(#fn_definition_args,)* )
146 -> Result<easy_jsonrpc_mw::BoundMethod<'static, #return_typ>, easy_jsonrpc_mw::ArgSerializeError> {
147 Ok(easy_jsonrpc_mw::BoundMethod::new(
148 #method_name_literal,
149 vec![ #(#args_serialize),* ],
150 ))
151 }
152 })
153}
154
155fn return_type_span(method: &MethodSig) -> Span {
156 let return_type = match &method.decl.output {
157 ReturnType::Default => None,
158 ReturnType::Type(_, typ) => Some(typ),
159 };
160 return_type
161 .map(|typ| typ.span())
162 .unwrap_or_else(|| method.decl.output.span().clone())
163}
164
165fn return_type(method: &MethodSig) -> Type {
166 match &method.decl.output {
167 ReturnType::Default => Type::Tuple(TypeTuple {
168 paren_token: Paren {
169 span: method.decl.output.span(),
170 },
171 elems: Punctuated::new(),
172 }),
173 ReturnType::Type(_, typ) => *typ.clone(),
174 }
175}
176
177fn trait_methods<'a>(tr: &'a ItemTrait) -> Result<Vec<&'a MethodSig>, Rejections> {
179 let methods = partition(tr.items.iter().map(|item| match item {
180 TraitItem::Method(method) => Ok(&method.sig),
181 other => Err(Rejection::create(other.span(), Reason::TraitNotStrictlyMethods).into()),
182 }))?;
183 partition(methods.iter().map(|method| {
184 if method.ident.to_string().starts_with("rpc.") {
185 Err(Rejection::create(method.ident.span(), Reason::ReservedMethodPrefix).into())
186 } else {
187 Ok(())
188 }
189 }))?;
190 Ok(methods)
191}
192
193fn add_handler(trait_name: &Ident, method: &MethodSig) -> Result<TokenStream, Rejections> {
195 let method_name = &method.ident;
196 let args = get_args(&method.decl)?;
197 let arg_name_literals = args.iter().map(|(id, _)| id.to_string());
198 let parse_args = args.iter().enumerate().map(|(index, (ident, ty))| {
199 let argname_literal = format!("\"{}\"", ident);
200 let prefix = match ty {
202 syn::Type::Reference(_) => quote! { & },
203 _ => quote! {},
204 };
205 quote_spanned! { ty.span() => #prefix {
206 let next_arg = ordered_args.next().expect(
207 "RPC method Got too few args. This is a bug." );
209 easy_jsonrpc_mw::serde_json::from_value(next_arg).map_err(|_| {
210 easy_jsonrpc_mw::InvalidArgs::InvalidArgStructure {
211 name: #argname_literal,
212 index: #index,
213 }.into()
214 })?
215 }}
216 });
217
218 Ok(quote! {{
219 let mut args: Vec<easy_jsonrpc_mw::Value> =
220 params.get_rpc_args(&[#(#arg_name_literals),*])
221 .map_err(|a| a.into())?;
222 let mut ordered_args = args.drain(..);
223 let res = <dyn #trait_name>::#method_name(self, #(#parse_args),*); debug_assert_eq!(ordered_args.next(), None); res
226 }})
227}
228
229fn get_args<'a>(method: &'a FnDecl) -> Result<Vec<(&'a Ident, &'a Type)>, Rejections> {
232 let mut inputs = method.inputs.iter();
233 match inputs.next() {
234 Some(FnArg::SelfRef(ArgSelfRef {
235 mutability: None, ..
236 })) => Ok(()),
237 Some(a) => Err(Rejection::create(a.span(), Reason::FirstArgumentNotSelfRef)),
238 None => Err(Rejection::create(
239 method.inputs.span(),
240 Reason::FirstArgumentNotSelfRef,
241 )),
242 }?;
243 partition(inputs.map(as_jsonrpc_arg))
244}
245
246fn partition<K, I: Iterator<Item = Result<K, Rejections>>>(iter: I) -> Result<Vec<K>, Rejections> {
248 let (min, _) = iter.size_hint();
249 let mut oks: Vec<K> = Vec::with_capacity(min);
250 let mut errs: Vec<Rejection> = Vec::new();
251 for i in iter {
252 match i {
253 Ok(ok) => oks.push(ok),
254 Err(Rejections { first, rest }) => {
255 errs.push(first);
256 errs.extend(rest);
257 }
258 }
259 }
260 match errs.first() {
261 Some(first) => Err(Rejections {
262 first: *first,
263 rest: errs[1..].to_vec(),
264 }),
265 None => Ok(oks),
266 }
267}
268
269fn as_jsonrpc_arg(arg: &FnArg) -> Result<(&Ident, &Type), Rejections> {
271 let arg = match arg {
272 FnArg::Captured(captured) => Ok(captured),
273 a => Err(Rejection::create(a.span(), Reason::ConcreteTypesRequired)),
274 }?;
275 let ty = &arg.ty;
276 let pat_ident = match &arg.pat {
277 Pat::Ident(pat_ident) => Ok(pat_ident),
278 a => Err(Rejection::create(a.span(), Reason::PatternMatchedArg)),
279 }?;
280 let ident = match pat_ident {
281 PatIdent {
282 by_ref: Some(r), ..
283 } => Err(Rejection::create(r.span(), Reason::ReferenceArg)),
284 PatIdent {
285 mutability: Some(m),
286 ..
287 } => Err(Rejection::create(m.span(), Reason::MutableArg)),
288 PatIdent {
289 subpat: Some((l, _)),
290 ..
291 } => Err(Rejection::create(l.span(), Reason::PatternMatchedArg)),
292 PatIdent {
293 ident,
294 by_ref: None,
295 mutability: None,
296 subpat: None,
297 } => Ok(ident),
298 }?;
299 Ok((&ident, &ty))
300}
301
302#[derive(Clone, Copy)]
304struct Rejection {
305 span: Span,
306 reason: Reason,
307}
308
309#[derive(Clone, Copy)]
311enum Reason {
312 FirstArgumentNotSelfRef,
313 PatternMatchedArg,
314 ConcreteTypesRequired,
315 TraitNotStrictlyMethods,
316 ReservedMethodPrefix,
317 ReferenceArg,
318 MutableArg,
319}
320
321struct Rejections {
324 first: Rejection, rest: Vec<Rejection>,
326}
327
328impl Rejections {
329 fn raise(self) -> TokenStream {
331 let Rejections { first, mut rest } = self;
332 let first_err = first.raise();
333 let rest_err = rest.drain(..).map(Rejection::raise);
334 quote! {
335 #first_err
336 #(#rest_err)*
337 }
338 }
339}
340
341impl Rejection {
351 fn create(span: Span, reason: Reason) -> Self {
352 Rejection { span, reason }
353 }
354
355 fn raise(self) -> TokenStream {
357 let description = match self.reason {
358 Reason::FirstArgumentNotSelfRef => "First argument to jsonrpc method must be &self.",
359 Reason::PatternMatchedArg => {
360 "Pattern matched arguments are not supported in jsonrpc methods."
361 }
362 Reason::ConcreteTypesRequired => {
363 "Arguments and return values must have concrete types."
364 }
365 Reason::TraitNotStrictlyMethods => {
366 "Macro 'jsonrpc_server' expects trait definition containing methods only."
367 }
368 Reason::ReservedMethodPrefix => {
369 "The prefix 'rpc.' is reserved https://www.jsonrpc.org/specification#request_object"
370 }
371 Reason::ReferenceArg => "Reference arguments not supported in jsonrpc macro.",
372 Reason::MutableArg => "Mutable arguments not supported in jsonrpc macro.",
373 };
374
375 syn::Error::new(self.span, description).to_compile_error()
376 }
377}
378
379impl From<Rejection> for Rejections {
380 fn from(first: Rejection) -> Rejections {
381 Rejections {
382 first,
383 rest: vec![],
384 }
385 }
386}