Skip to content

Commit

Permalink
Implement server side named parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
luc65r committed Aug 26, 2021
1 parent 17883c0 commit 03b0feb
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 52 deletions.
5 changes: 1 addition & 4 deletions derive/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,7 @@ impl DeriveOptions {
options.enable_client = true;
options.enable_server = true;
}
if options.enable_server && options.params_style == ParamStyle::Named {
// This is not allowed at this time
panic!("Server code generation only supports `params = \"positional\"` (default) or `params = \"raw\" at this time.")
}

Ok(options)
}
}
14 changes: 0 additions & 14 deletions derive/src/rpc_trait.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::options::DeriveOptions;
use crate::params_style::ParamStyle;
use crate::rpc_attr::{AttributeKind, PubSubMethodKind, RpcMethodAttribute};
use crate::to_client::generate_client_module;
use crate::to_delegate::{generate_trait_item_method, MethodRegistration, RpcMethod};
Expand All @@ -22,10 +21,6 @@ const MISSING_UNSUBSCRIBE_METHOD_ERR: &str =
"Can't find unsubscribe method, expected a method annotated with `unsubscribe` \
e.g. `#[pubsub(subscription = \"hello\", unsubscribe, name = \"hello_unsubscribe\")]`";

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

const RPC_MOD_NAME_PREFIX: &str = "rpc_impl_";

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

fn has_named_params(methods: &[RpcMethod]) -> bool {
methods
.iter()
.any(|method| method.attr.params_style == Some(ParamStyle::Named))
}

pub fn crate_name(name: &str) -> Result<Ident> {
proc_macro_crate::crate_name(name)
.map(|name| Ident::new(&name, Span::call_site()))
Expand Down Expand Up @@ -264,9 +253,6 @@ pub fn rpc_impl(input: syn::Item, options: &DeriveOptions) -> Result<proc_macro2
});
}
if options.enable_server {
if has_named_params(&methods) {
return Err(syn::Error::new_spanned(rpc_trait, USING_NAMED_PARAMS_WITH_SERVER_ERR));
}
let rpc_server_module = generate_server_module(&method_registrations, &rpc_trait, &methods)?;
submodules.push(rpc_server_module);
exports.push(quote! {
Expand Down
27 changes: 26 additions & 1 deletion derive/src/to_delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,21 @@ impl RpcMethod {
})
.collect();

let arg_names: Vec<_> = self
.trait_item
.sig
.inputs
.iter()
.cloned()
.filter_map(|arg| match arg {
syn::FnArg::Typed(pat) => match *pat.pat {
syn::Pat::Ident(pat) => Some(pat.ident),
_ => None,
},
_ => None,
})
.collect();

// special args are those which are not passed directly via rpc params: metadata, subscriber
let special_args = Self::special_args(&param_types);
param_types.retain(|ty| !special_args.iter().any(|(_, sty)| sty == ty));
Expand Down Expand Up @@ -246,7 +261,17 @@ impl RpcMethod {
} else if self.attr.params_style == Some(ParamStyle::Positional) {
quote! { let params = params.parse::<(#(#param_types, )*)>(); }
} else {
unimplemented!("Server side named parameters are not implemented");
quote! {
#[derive(serde::Deserialize)]
#[allow(non_camel_case_types)]
struct __Params {
#(
#arg_names: #param_types,
)*
}
let params = params.parse::<__Params>()
.map(|__Params { #(#arg_names, )* }| (#(#arg_names, )*));
}
}
};

Expand Down
35 changes: 35 additions & 0 deletions derive/tests/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ pub trait Rpc {
#[rpc(name = "raw", params = "raw")]
fn raw(&self, params: Params) -> Result<String>;

#[rpc(name = "named_add", params = "named")]
fn named_add(&self, a: u64, b: u64) -> Result<u64>;

/// Handles a notification.
#[rpc(name = "notify")]
fn notify(&self, a: u64);
Expand All @@ -55,6 +58,10 @@ impl Rpc for RpcImpl {
Ok("OK".into())
}

fn named_add(&self, a: u64, b: u64) -> Result<u64> {
Ok(a + b)
}

fn notify(&self, a: u64) {
println!("Received `notify` with value: {}", a);
}
Expand Down Expand Up @@ -222,6 +229,34 @@ fn should_accept_any_raw_params() {
assert_eq!(expected, result4);
}

#[test]
fn should_accept_named_params() {
let mut io = IoHandler::new();
let rpc = RpcImpl::default();
io.extend_with(rpc.to_delegate());

// when
let req1 = r#"{"jsonrpc":"2.0","id":1,"method":"named_add","params":{"a":1,"b":2}}"#;
let req2 = r#"{"jsonrpc":"2.0","id":1,"method":"named_add","params":{"b":2,"a":1}}"#;

let res1 = io.handle_request_sync(req1);
let res2 = io.handle_request_sync(req2);

let expected = r#"{
"jsonrpc": "2.0",
"result": 3,
"id": 1
}"#;
let expected: Response = serde_json::from_str(expected).unwrap();

// then
let result1: Response = serde_json::from_str(&res1.unwrap()).unwrap();
assert_eq!(expected, result1);

let result2: Response = serde_json::from_str(&res2.unwrap()).unwrap();
assert_eq!(expected, result2);
}

#[test]
fn should_accept_only_notifications() {
let mut io = IoHandler::new();
Expand Down
10 changes: 0 additions & 10 deletions derive/tests/ui/attr-named-params-on-server.rs

This file was deleted.

9 changes: 0 additions & 9 deletions derive/tests/ui/attr-named-params-on-server.stderr

This file was deleted.

7 changes: 0 additions & 7 deletions derive/tests/ui/trait-attr-named-params-on-server.rs

This file was deleted.

7 changes: 0 additions & 7 deletions derive/tests/ui/trait-attr-named-params-on-server.stderr

This file was deleted.

0 comments on commit 03b0feb

Please sign in to comment.