Skip to content

Commit 6ae633c

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

8 files changed

+172
-106
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

+76-49
Original file line numberDiff line numberDiff line change
@@ -206,21 +206,37 @@ 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()
215215
.filter_map(|arg| match arg {
216-
syn::FnArg::Typed(ty) => Some(*ty.ty),
216+
syn::FnArg::Typed(pat_type) => Some(pat_type),
217217
_ => None,
218218
})
219-
.collect();
219+
.enumerate();
220+
221+
let (special_args, fn_args) = {
222+
// special args are those which are not passed directly via rpc params: metadata, subscriber
223+
let mut special_args = vec![];
224+
let mut fn_args = vec![];
225+
226+
for (i, arg) in args {
227+
if let Some(sarg) = Self::special_arg(i, arg.clone()) {
228+
special_args.push(sarg);
229+
} else {
230+
fn_args.push(arg);
231+
}
232+
}
233+
234+
(special_args, fn_args)
235+
};
236+
237+
let param_types: Vec<_> = fn_args.iter().map(|arg| *arg.ty.clone()).collect();
238+
let arg_names: Vec<_> = fn_args.iter().map(|arg| *arg.pat.clone()).collect();
220239

221-
// 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));
224240
if param_types.len() > TUPLE_FIELD_NAMES.len() {
225241
return Err(syn::Error::new_spanned(
226242
&self.trait_item,
@@ -232,28 +248,38 @@ impl RpcMethod {
232248
.take(param_types.len())
233249
.map(|name| ident(name))
234250
.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");
251+
let parse_params = if param_types.is_empty() {
252+
quote! { let params = params.expect_no_params(); }
253+
} else {
254+
match self.attr.params_style.as_ref().unwrap() {
255+
ParamStyle::Raw => quote! { let params: _jsonrpc_core::Result<_> = Ok((params,)); },
256+
ParamStyle::Positional => {
257+
// last arguments that are `Option`-s are optional 'trailing' arguments
258+
let trailing_args_num = param_types.iter().rev().take_while(|t| is_option_type(t)).count();
259+
if trailing_args_num != 0 {
260+
self.params_with_trailing(trailing_args_num, &param_types, tuple_fields)
261+
} else {
262+
quote! { let params = params.parse::<(#(#param_types, )*)>(); }
263+
}
264+
}
265+
ParamStyle::Named => quote! {
266+
#[derive(serde::Deserialize)]
267+
#[allow(non_camel_case_types)]
268+
struct __Params {
269+
#(
270+
#fn_args,
271+
)*
272+
}
273+
let params = params.parse::<__Params>()
274+
.map(|__Params { #(#arg_names, )* }| (#(#arg_names, )*));
275+
},
250276
}
251277
};
252278

253279
let method_ident = self.ident();
254280
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();
281+
let extra_closure_args: Vec<_> = special_args.iter().map(|arg| *arg.pat.clone()).collect();
282+
let extra_method_types: Vec<_> = special_args.iter().map(|arg| *arg.ty.clone()).collect();
257283

258284
let closure_args = quote! { base, params, #(#extra_closure_args), * };
259285
let method_sig = quote! { fn(&Self, #(#extra_method_types, ) * #(#param_types), *) #result };
@@ -301,34 +327,35 @@ impl RpcMethod {
301327
})
302328
}
303329

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
330+
fn special_arg(index: usize, arg: syn::PatType) -> Option<syn::PatType> {
331+
match index {
332+
0 if arg.ty == parse_quote!(Self::Metadata) => Some(syn::PatType {
333+
pat: Box::new(syn::Pat::Ident(syn::PatIdent {
334+
attrs: vec![],
335+
by_ref: None,
336+
mutability: None,
337+
ident: ident(METADATA_CLOSURE_ARG),
338+
subpat: None,
339+
})),
340+
..arg
341+
}),
342+
1 => match *arg.ty {
343+
syn::Type::Path(ref path) if path.path.segments.iter().any(|s| s.ident == SUBSCRIBER_TYPE_IDENT) => {
344+
Some(syn::PatType {
345+
pat: Box::new(syn::Pat::Ident(syn::PatIdent {
346+
attrs: vec![],
347+
by_ref: None,
348+
mutability: None,
349+
ident: ident(SUBSCRIBER_CLOSURE_ARG),
350+
subpat: None,
351+
})),
352+
..arg
353+
})
318354
}
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));
355+
_ => None,
356+
},
357+
_ => None,
330358
}
331-
special_args
332359
}
333360

334361
fn params_with_trailing(

derive/tests/macros.rs

+95-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use jsonrpc_core::types::params::Params;
22
use jsonrpc_core::{IoHandler, Response};
33
use jsonrpc_derive::rpc;
4+
use serde;
45
use serde_json;
56

67
pub enum MyError {}
@@ -14,6 +15,8 @@ type Result<T> = ::std::result::Result<T, MyError>;
1415

1516
#[rpc]
1617
pub trait Rpc {
18+
type Metadata;
19+
1720
/// Returns a protocol version.
1821
#[rpc(name = "protocolVersion")]
1922
fn protocol_version(&self) -> Result<String>;
@@ -30,6 +33,18 @@ pub trait Rpc {
3033
#[rpc(name = "raw", params = "raw")]
3134
fn raw(&self, params: Params) -> Result<String>;
3235

36+
/// Adds two numbers and returns a result.
37+
#[rpc(name = "named_add", params = "named")]
38+
fn named_add(&self, a: u64, b: u64) -> Result<u64>;
39+
40+
/// Adds one or two numbers and returns a result.
41+
#[rpc(name = "option_named_add", params = "named")]
42+
fn option_named_add(&self, a: u64, b: Option<u64>) -> Result<u64>;
43+
44+
/// Adds two numbers and returns a result.
45+
#[rpc(meta, name = "meta_named_add", params = "named")]
46+
fn meta_named_add(&self, meta: Self::Metadata, a: u64, b: u64) -> Result<u64>;
47+
3348
/// Handles a notification.
3449
#[rpc(name = "notify")]
3550
fn notify(&self, a: u64);
@@ -39,6 +54,8 @@ pub trait Rpc {
3954
struct RpcImpl;
4055

4156
impl Rpc for RpcImpl {
57+
type Metadata = Metadata;
58+
4259
fn protocol_version(&self) -> Result<String> {
4360
Ok("version1".into())
4461
}
@@ -55,14 +72,30 @@ impl Rpc for RpcImpl {
5572
Ok("OK".into())
5673
}
5774

75+
fn named_add(&self, a: u64, b: u64) -> Result<u64> {
76+
Ok(a + b)
77+
}
78+
79+
fn option_named_add(&self, a: u64, b: Option<u64>) -> Result<u64> {
80+
Ok(a + b.unwrap_or_default())
81+
}
82+
83+
fn meta_named_add(&self, _meta: Self::Metadata, a: u64, b: u64) -> Result<u64> {
84+
Ok(a + b)
85+
}
86+
5887
fn notify(&self, a: u64) {
5988
println!("Received `notify` with value: {}", a);
6089
}
6190
}
6291

92+
#[derive(Clone, Default)]
93+
struct Metadata;
94+
impl jsonrpc_core::Metadata for Metadata {}
95+
6396
#[test]
6497
fn should_accept_empty_array_as_no_params() {
65-
let mut io = IoHandler::new();
98+
let mut io = IoHandler::default();
6699
let rpc = RpcImpl::default();
67100
io.extend_with(rpc.to_delegate());
68101

@@ -94,7 +127,7 @@ fn should_accept_empty_array_as_no_params() {
94127

95128
#[test]
96129
fn should_accept_single_param() {
97-
let mut io = IoHandler::new();
130+
let mut io = IoHandler::default();
98131
let rpc = RpcImpl::default();
99132
io.extend_with(rpc.to_delegate());
100133

@@ -120,7 +153,7 @@ fn should_accept_single_param() {
120153

121154
#[test]
122155
fn should_accept_multiple_params() {
123-
let mut io = IoHandler::new();
156+
let mut io = IoHandler::default();
124157
let rpc = RpcImpl::default();
125158
io.extend_with(rpc.to_delegate());
126159

@@ -146,7 +179,7 @@ fn should_accept_multiple_params() {
146179

147180
#[test]
148181
fn should_use_method_name_aliases() {
149-
let mut io = IoHandler::new();
182+
let mut io = IoHandler::default();
150183
let rpc = RpcImpl::default();
151184
io.extend_with(rpc.to_delegate());
152185

@@ -187,7 +220,7 @@ fn should_use_method_name_aliases() {
187220

188221
#[test]
189222
fn should_accept_any_raw_params() {
190-
let mut io = IoHandler::new();
223+
let mut io = IoHandler::default();
191224
let rpc = RpcImpl::default();
192225
io.extend_with(rpc.to_delegate());
193226

@@ -222,9 +255,65 @@ fn should_accept_any_raw_params() {
222255
assert_eq!(expected, result4);
223256
}
224257

258+
#[test]
259+
fn should_accept_named_params() {
260+
let mut io = IoHandler::default();
261+
let rpc = RpcImpl::default();
262+
io.extend_with(rpc.to_delegate());
263+
264+
// when
265+
let req1 = r#"{"jsonrpc":"2.0","id":1,"method":"named_add","params":{"a":1,"b":2}}"#;
266+
let req2 = r#"{"jsonrpc":"2.0","id":1,"method":"named_add","params":{"b":2,"a":1}}"#;
267+
268+
let res1 = io.handle_request_sync(req1);
269+
let res2 = io.handle_request_sync(req2);
270+
271+
let expected = r#"{
272+
"jsonrpc": "2.0",
273+
"result": 3,
274+
"id": 1
275+
}"#;
276+
let expected: Response = serde_json::from_str(expected).unwrap();
277+
278+
// then
279+
let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap();
280+
assert_eq!(expected, result1);
281+
282+
let result2: Response = serde_json::from_str(&res2.unwrap()).unwrap();
283+
assert_eq!(expected, result2);
284+
}
285+
286+
#[test]
287+
fn should_accept_option_named_params() {
288+
let mut io = IoHandler::default();
289+
let rpc = RpcImpl::default();
290+
io.extend_with(rpc.to_delegate());
291+
292+
// when
293+
let req1 = r#"{"jsonrpc":"2.0","id":1,"method":"option_named_add","params":{"a":1,"b":2}}"#;
294+
let req2 = r#"{"jsonrpc":"2.0","id":1,"method":"option_named_add","params":{"a":3}}"#;
295+
296+
let res1 = io.handle_request_sync(req1);
297+
let res2 = io.handle_request_sync(req2);
298+
299+
let expected = r#"{
300+
"jsonrpc": "2.0",
301+
"result": 3,
302+
"id": 1
303+
}"#;
304+
let expected: Response = serde_json::from_str(expected).unwrap();
305+
306+
// then
307+
let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap();
308+
assert_eq!(expected, result1);
309+
310+
let result2: Response = serde_json::from_str(&res2.unwrap()).unwrap();
311+
assert_eq!(expected, result2);
312+
}
313+
225314
#[test]
226315
fn should_accept_only_notifications() {
227-
let mut io = IoHandler::new();
316+
let mut io = IoHandler::default();
228317
let rpc = RpcImpl::default();
229318
io.extend_with(rpc.to_delegate());
230319

0 commit comments

Comments
 (0)