Skip to content

Commit d35c973

Browse files
committed
Implement server side named parameters
1 parent 17883c0 commit d35c973

8 files changed

+216
-107
lines changed

derive/src/options.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,7 @@ impl DeriveOptions {
6262
options.enable_client = true;
6363
options.enable_server = true;
6464
}
65-
if options.enable_server && options.params_style == ParamStyle::Named {
66-
// This is not allowed at this time
67-
panic!("Server code generation only supports `params = \"positional\"` (default) or `params = \"raw\" at this time.")
68-
}
65+
6966
Ok(options)
7067
}
7168
}

derive/src/rpc_trait.rs

-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::options::DeriveOptions;
2-
use crate::params_style::ParamStyle;
32
use crate::rpc_attr::{AttributeKind, PubSubMethodKind, RpcMethodAttribute};
43
use crate::to_client::generate_client_module;
54
use crate::to_delegate::{generate_trait_item_method, MethodRegistration, RpcMethod};
@@ -22,10 +21,6 @@ const MISSING_UNSUBSCRIBE_METHOD_ERR: &str =
2221
"Can't find unsubscribe method, expected a method annotated with `unsubscribe` \
2322
e.g. `#[pubsub(subscription = \"hello\", unsubscribe, name = \"hello_unsubscribe\")]`";
2423

25-
pub const USING_NAMED_PARAMS_WITH_SERVER_ERR: &str =
26-
"`params = \"named\"` can only be used to generate a client (on a trait annotated with #[rpc(client)]). \
27-
At this time the server does not support named parameters.";
28-
2924
const RPC_MOD_NAME_PREFIX: &str = "rpc_impl_";
3025

3126
struct RpcTrait {
@@ -222,12 +217,6 @@ fn rpc_wrapper_mod_name(rpc_trait: &syn::ItemTrait) -> syn::Ident {
222217
syn::Ident::new(&mod_name, proc_macro2::Span::call_site())
223218
}
224219

225-
fn has_named_params(methods: &[RpcMethod]) -> bool {
226-
methods
227-
.iter()
228-
.any(|method| method.attr.params_style == Some(ParamStyle::Named))
229-
}
230-
231220
pub fn crate_name(name: &str) -> Result<Ident> {
232221
proc_macro_crate::crate_name(name)
233222
.map(|name| Ident::new(&name, Span::call_site()))
@@ -264,9 +253,6 @@ pub fn rpc_impl(input: syn::Item, options: &DeriveOptions) -> Result<proc_macro2
264253
});
265254
}
266255
if options.enable_server {
267-
if has_named_params(&methods) {
268-
return Err(syn::Error::new_spanned(rpc_trait, USING_NAMED_PARAMS_WITH_SERVER_ERR));
269-
}
270256
let rpc_server_module = generate_server_module(&method_registrations, &rpc_trait, &methods)?;
271257
submodules.push(rpc_server_module);
272258
exports.push(quote! {

derive/src/to_delegate.rs

+79-50
Original file line numberDiff line numberDiff line change
@@ -206,21 +206,39 @@ impl RpcMethod {
206206
}
207207

208208
fn generate_delegate_closure(&self, is_subscribe: bool) -> Result<proc_macro2::TokenStream> {
209-
let mut param_types: Vec<_> = self
209+
let args = self
210210
.trait_item
211211
.sig
212212
.inputs
213213
.iter()
214214
.cloned()
215-
.filter_map(|arg| match arg {
216-
syn::FnArg::Typed(ty) => Some(*ty.ty),
217-
_ => None,
215+
.filter_map(|arg| {
216+
if let syn::FnArg::Typed(pat_type) = arg {
217+
Some(pat_type)
218+
} else {
219+
None
220+
}
218221
})
219-
.collect();
222+
.enumerate();
220223

221224
// special args are those which are not passed directly via rpc params: metadata, subscriber
222-
let special_args = Self::special_args(&param_types);
223-
param_types.retain(|ty| !special_args.iter().any(|(_, sty)| sty == ty));
225+
let mut special_args = vec![];
226+
let mut fn_args = vec![];
227+
228+
for (i, arg) in args {
229+
if let Some(sarg) = Self::special_arg(i, arg.clone()) {
230+
special_args.push(sarg);
231+
} else {
232+
fn_args.push(arg);
233+
}
234+
}
235+
236+
let special_args: Vec<_> = special_args;
237+
let fn_args: Vec<_> = fn_args;
238+
239+
let param_types: Vec<_> = fn_args.iter().cloned().map(|arg| *arg.ty).collect();
240+
let arg_names: Vec<_> = fn_args.iter().cloned().map(|arg| *arg.pat).collect();
241+
224242
if param_types.len() > TUPLE_FIELD_NAMES.len() {
225243
return Err(syn::Error::new_spanned(
226244
&self.trait_item,
@@ -232,28 +250,38 @@ impl RpcMethod {
232250
.take(param_types.len())
233251
.map(|name| ident(name))
234252
.collect());
235-
let param_types = &param_types;
236-
let parse_params = {
237-
// last arguments that are `Option`-s are optional 'trailing' arguments
238-
let trailing_args_num = param_types.iter().rev().take_while(|t| is_option_type(t)).count();
239-
240-
if trailing_args_num != 0 {
241-
self.params_with_trailing(trailing_args_num, param_types, tuple_fields)
242-
} else if param_types.is_empty() {
243-
quote! { let params = params.expect_no_params(); }
244-
} else if self.attr.params_style == Some(ParamStyle::Raw) {
245-
quote! { let params: _jsonrpc_core::Result<_> = Ok((params,)); }
246-
} else if self.attr.params_style == Some(ParamStyle::Positional) {
247-
quote! { let params = params.parse::<(#(#param_types, )*)>(); }
248-
} else {
249-
unimplemented!("Server side named parameters are not implemented");
253+
let parse_params = if param_types.is_empty() {
254+
quote! { let params = params.expect_no_params(); }
255+
} else {
256+
match self.attr.params_style.as_ref().unwrap() {
257+
ParamStyle::Raw => quote! { let params: _jsonrpc_core::Result<_> = Ok((params,)); },
258+
ParamStyle::Positional => {
259+
// last arguments that are `Option`-s are optional 'trailing' arguments
260+
let trailing_args_num = param_types.iter().rev().take_while(|t| is_option_type(t)).count();
261+
if trailing_args_num != 0 {
262+
self.params_with_trailing(trailing_args_num, &param_types, tuple_fields)
263+
} else {
264+
quote! { let params = params.parse::<(#(#param_types, )*)>(); }
265+
}
266+
}
267+
ParamStyle::Named => quote! {
268+
#[derive(serde::Deserialize)]
269+
#[allow(non_camel_case_types)]
270+
struct __Params {
271+
#(
272+
#fn_args,
273+
)*
274+
}
275+
let params = params.parse::<__Params>()
276+
.map(|__Params { #(#arg_names, )* }| (#(#arg_names, )*));
277+
},
250278
}
251279
};
252280

253281
let method_ident = self.ident();
254282
let result = &self.trait_item.sig.output;
255-
let extra_closure_args: &Vec<_> = &special_args.iter().cloned().map(|arg| arg.0).collect();
256-
let extra_method_types: &Vec<_> = &special_args.iter().cloned().map(|arg| arg.1).collect();
283+
let extra_closure_args: Vec<_> = special_args.iter().cloned().map(|arg| *arg.pat).collect();
284+
let extra_method_types: Vec<_> = special_args.iter().cloned().map(|arg| *arg.ty).collect();
257285

258286
let closure_args = quote! { base, params, #(#extra_closure_args), * };
259287
let method_sig = quote! { fn(&Self, #(#extra_method_types, ) * #(#param_types), *) #result };
@@ -301,34 +329,35 @@ impl RpcMethod {
301329
})
302330
}
303331

304-
fn special_args(param_types: &[syn::Type]) -> Vec<(syn::Ident, syn::Type)> {
305-
let meta_arg = param_types.first().and_then(|ty| {
306-
if *ty == parse_quote!(Self::Metadata) {
307-
Some(ty.clone())
308-
} else {
309-
None
310-
}
311-
});
312-
let subscriber_arg = param_types.get(1).and_then(|ty| {
313-
if let syn::Type::Path(path) = ty {
314-
if path.path.segments.iter().any(|s| s.ident == SUBSCRIBER_TYPE_IDENT) {
315-
Some(ty.clone())
316-
} else {
317-
None
332+
fn special_arg(index: usize, arg: syn::PatType) -> Option<syn::PatType> {
333+
match index {
334+
0 if arg.ty == parse_quote!(Self::Metadata) => Some(syn::PatType {
335+
pat: Box::new(syn::Pat::Ident(syn::PatIdent {
336+
attrs: vec![],
337+
by_ref: None,
338+
mutability: None,
339+
ident: ident(METADATA_CLOSURE_ARG),
340+
subpat: None,
341+
})),
342+
..arg
343+
}),
344+
1 => match *arg.ty {
345+
syn::Type::Path(ref path) if path.path.segments.iter().any(|s| s.ident == SUBSCRIBER_TYPE_IDENT) => {
346+
Some(syn::PatType {
347+
pat: Box::new(syn::Pat::Ident(syn::PatIdent {
348+
attrs: vec![],
349+
by_ref: None,
350+
mutability: None,
351+
ident: ident(SUBSCRIBER_CLOSURE_ARG),
352+
subpat: None,
353+
})),
354+
..arg
355+
})
318356
}
319-
} else {
320-
None
321-
}
322-
});
323-
324-
let mut special_args = Vec::new();
325-
if let Some(meta) = meta_arg {
326-
special_args.push((ident(METADATA_CLOSURE_ARG), meta));
327-
}
328-
if let Some(subscriber) = subscriber_arg {
329-
special_args.push((ident(SUBSCRIBER_CLOSURE_ARG), subscriber));
357+
_ => None,
358+
},
359+
_ => None,
330360
}
331-
special_args
332361
}
333362

334363
fn params_with_trailing(

0 commit comments

Comments
 (0)