From 4bf380df426dc01fd2583d10b69455c75f4a3e59 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Tue, 18 Feb 2020 09:39:54 +1100 Subject: [PATCH 01/26] add support for json objects in TypedClient.call_method --- core-client/transports/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core-client/transports/src/lib.rs b/core-client/transports/src/lib.rs index ab5a4bc6f..b6da7c7e2 100644 --- a/core-client/transports/src/lib.rs +++ b/core-client/transports/src/lib.rs @@ -303,9 +303,10 @@ impl TypedClient { let params = match args { Value::Array(vec) => Params::Array(vec), Value::Null => Params::None, + Value::Object(map) => Params::Map(map), _ => { return future::Either::A(future::err(RpcError::Other(format_err!( - "RPC params should serialize to a JSON array, or null" + "RPC params should serialize to a JSON array, JSON object or null" )))) } }; From c65efae902cabbe2245d2d979b34a0a1f14e4a51 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Tue, 18 Feb 2020 09:53:03 +1100 Subject: [PATCH 02/26] adds named params switch to #[rpc] method annotation --- derive/src/rpc_attr.rs | 7 ++++++- derive/tests/ui/attr-invalid-meta-words.stderr | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index 95a7c1286..0912b792f 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -10,6 +10,7 @@ pub struct RpcMethodAttribute { pub aliases: Vec, pub kind: AttributeKind, pub raw_params: bool, + pub named_params: bool, } #[derive(Clone, Debug)] @@ -38,6 +39,7 @@ const ALIASES_KEY: &str = "alias"; const PUB_SUB_ATTR_NAME: &str = "pubsub"; const METADATA_META_WORD: &str = "meta"; const RAW_PARAMS_META_WORD: &str = "raw_params"; +const USE_NAMED_PARAMS_META_WORD: &str = "named_params"; const SUBSCRIBE_META_WORD: &str = "subscribe"; const UNSUBSCRIBE_META_WORD: &str = "unsubscribe"; const RETURNS_META_WORD: &str = "returns"; @@ -81,12 +83,15 @@ impl RpcMethodAttribute { let aliases = get_meta_list(&meta).map_or(Vec::new(), |ml| get_aliases(ml)); let raw_params = get_meta_list(meta).map_or(false, |ml| has_meta_word(RAW_PARAMS_META_WORD, ml)); + let named_params = + get_meta_list(meta).map_or(false, |ml| has_meta_word(USE_NAMED_PARAMS_META_WORD, ml)); Ok(RpcMethodAttribute { attr: attr.clone(), name, aliases, kind, raw_params, + named_params, }) }) }) @@ -178,7 +183,7 @@ fn validate_attribute_meta(meta: syn::Meta) -> Result { let ident = path_to_str(meta.path()); match ident.as_ref().map(String::as_str) { Some(RPC_ATTR_NAME) => { - validate_idents(&meta, &visitor.meta_words, &[METADATA_META_WORD, RAW_PARAMS_META_WORD])?; + validate_idents(&meta, &visitor.meta_words, &[METADATA_META_WORD, RAW_PARAMS_META_WORD, USE_NAMED_PARAMS_META_WORD])?; validate_idents(&meta, &visitor.name_value_names, &[RPC_NAME_KEY, RETURNS_META_WORD])?; validate_idents(&meta, &visitor.meta_list_names, &[ALIASES_KEY]) } diff --git a/derive/tests/ui/attr-invalid-meta-words.stderr b/derive/tests/ui/attr-invalid-meta-words.stderr index 7b5d63d17..d4a8848e5 100644 --- a/derive/tests/ui/attr-invalid-meta-words.stderr +++ b/derive/tests/ui/attr-invalid-meta-words.stderr @@ -1,4 +1,4 @@ -error: Invalid attribute parameter(s): 'Xmeta'. Expected 'meta, raw_params' +error: Invalid attribute parameter(s): 'Xmeta'. Expected 'meta, raw_params, named_params' --> $DIR/attr-invalid-meta-words.rs:5:2 | 5 | /// Returns a protocol version From 187078a05817929b573ef8c8c0fd146005d2946a Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Tue, 18 Feb 2020 10:44:31 +1100 Subject: [PATCH 03/26] add generating of map style params in macro --- derive/src/to_client.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/derive/src/to_client.rs b/derive/src/to_client.rs index f622a3b96..329078fde 100644 --- a/derive/src/to_client.rs +++ b/derive/src/to_client.rs @@ -100,11 +100,23 @@ fn generate_client_methods(methods: &[MethodRegistration]) -> Result continue, }; let returns_str = quote!(#returns).to_string(); + + let args_serialized = match method.attr.named_params { + true => quote! { // use object style serialization with field names taken from the function param names + serde_json::json!({ + #(stringify!(#arg_names): #arg_names,)* + }) + }, + false => quote! { // use tuple style serialization + (#(#arg_names,)*) + }, + }; + let client_method = syn::parse_quote! { #(#attrs)* pub fn #name(&self, #args) -> impl Future { - let args_tuple = (#(#arg_names,)*); - self.inner.call_method(#rpc_name, #returns_str, args_tuple) + let args = #args_serialized; + self.inner.call_method(#rpc_name, #returns_str, args) } }; client_methods.push(client_method); From 167612fecd2bbbe3e9d538bcab10892bb00674f4 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Tue, 18 Feb 2020 11:02:03 +1100 Subject: [PATCH 04/26] prevent creating a server when using the named_params switch --- derive/src/rpc_trait.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index 737c18665..b5508d9a6 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -218,6 +218,12 @@ 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.named_params + }) +} + pub fn crate_name(name: &str) -> Result { proc_macro_crate::crate_name(name) .map(|name| Ident::new(&name, Span::call_site())) @@ -252,6 +258,14 @@ pub fn rpc_impl(input: syn::Item, options: DeriveOptions) -> Result Date: Tue, 18 Feb 2020 11:09:03 +1100 Subject: [PATCH 05/26] add test for attempt to derive with named_params on server. refactors error string --- derive/src/rpc_trait.rs | 8 +++++--- derive/tests/ui/attr-named-params-on-server.rs | 10 ++++++++++ derive/tests/ui/attr-named-params-on-server.stderr | 9 +++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 derive/tests/ui/attr-named-params-on-server.rs create mode 100644 derive/tests/ui/attr-named-params-on-server.stderr diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index b5508d9a6..171887748 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -21,6 +21,10 @@ 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\")]`"; +const USING_NAMED_PARAMS_WITH_SERVER_ERR: &str = + "The named_params switch 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 { @@ -261,9 +265,7 @@ pub fn rpc_impl(input: syn::Item, options: DeriveOptions) -> Result Result; +} + +fn main() {} diff --git a/derive/tests/ui/attr-named-params-on-server.stderr b/derive/tests/ui/attr-named-params-on-server.stderr new file mode 100644 index 000000000..ebb0db78e --- /dev/null +++ b/derive/tests/ui/attr-named-params-on-server.stderr @@ -0,0 +1,9 @@ +error: The named_params switch 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. + --> $DIR/attr-named-params-on-server.rs:4:1 + | +4 | / pub trait Rpc { +5 | | /// Returns a protocol version +6 | | #[rpc(name = "add", named_params)] +7 | | fn add(&self, a: u32, b: u32) -> Result; +8 | | } + | |_^ From f158a7f5241bdac799c38008051f32ed17335d4e Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Tue, 18 Feb 2020 12:04:00 +1100 Subject: [PATCH 06/26] adds test that correct params object is generated --- derive/tests/client.rs | 113 ++++++++++++++++++++++++++++++----------- 1 file changed, 83 insertions(+), 30 deletions(-) diff --git a/derive/tests/client.rs b/derive/tests/client.rs index 469175cdf..c8fee2a1f 100644 --- a/derive/tests/client.rs +++ b/derive/tests/client.rs @@ -3,43 +3,96 @@ use jsonrpc_core::{IoHandler, Result}; use jsonrpc_core_client::transports::local; use jsonrpc_derive::rpc; -#[rpc] -pub trait Rpc { - #[rpc(name = "add")] - fn add(&self, a: u64, b: u64) -> Result; +mod client_server { + use super::*; - #[rpc(name = "notify")] - fn notify(&self, foo: u64); -} + #[rpc] + pub trait Rpc { + #[rpc(name = "add")] + fn add(&self, a: u64, b: u64) -> Result; + + #[rpc(name = "notify")] + fn notify(&self, foo: u64); + } + + struct RpcServer; -struct RpcServer; + impl Rpc for RpcServer { + fn add(&self, a: u64, b: u64) -> Result { + Ok(a + b) + } -impl Rpc for RpcServer { - fn add(&self, a: u64, b: u64) -> Result { - Ok(a + b) + fn notify(&self, foo: u64) { + println!("received {}", foo); + } } - fn notify(&self, foo: u64) { - println!("received {}", foo); + #[test] + fn client_server_roundtrip() { + let mut handler = IoHandler::new(); + handler.extend_with(RpcServer.to_delegate()); + let (client, rpc_client) = local::connect::(handler); + let fut = client + .clone() + .add(3, 4) + .and_then(move |res| client.notify(res).map(move |_| res)) + .join(rpc_client) + .map(|(res, ())| { + assert_eq!(res, 7); + }) + .map_err(|err| { + eprintln!("{:?}", err); + assert!(false); + }); + tokio::run(fut); } + } -#[test] -fn client_server_roundtrip() { - let mut handler = IoHandler::new(); - handler.extend_with(RpcServer.to_delegate()); - let (client, rpc_client) = local::connect::(handler); - let fut = client - .clone() - .add(3, 4) - .and_then(move |res| client.notify(res).map(move |_| res)) - .join(rpc_client) - .map(|(res, ())| { - assert_eq!(res, 7); - }) - .map_err(|err| { - eprintln!("{:?}", err); - assert!(false); + +mod named_params { + use super::*; + use jsonrpc_core::Params; + use serde_json::json; + + #[rpc(client)] + pub trait Rpc { + #[rpc(name = "call_with_named", named_params)] + fn call_with_named(&self, number: u64, string: String, json: serde_json::Value) -> Result; + + #[rpc(name = "notify")] + fn notify(&self, payload: serde_json::Value); + } + + #[test] + fn client_generates_correct_named_params_payload() { + let expected = json!({ // key names are derived from function parameter names in the trait + "number": 3, + "string": String::from("test string"), + "json": { + "key": ["value"] + } + }); + + let mut handler = IoHandler::new(); + handler.add_method("call_with_named", |params: Params| { + Ok(params.into()) }); - tokio::run(fut); + + let (client, rpc_client) = local::connect::(handler); + let fut = client + .clone() + .call_with_named(3, String::from("test string"), json!({"key": ["value"]})) + .and_then(move |res| client.notify(res.clone()).map(move |_| res)) + .join(rpc_client) + .map(move |(res, ())| { + assert_eq!(res, expected); + }) + .map_err(|err| { + eprintln!("{:?}", err); + assert!(false); + }); + tokio::run(fut); + } + } From b0fefbcae908ad909f2d176e98c86a398cb5a83b Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Tue, 18 Feb 2020 12:04:18 +1100 Subject: [PATCH 07/26] apply cargo fmt --- derive/src/rpc_attr.rs | 10 +++++++--- derive/src/rpc_trait.rs | 9 ++------- derive/src/to_client.rs | 14 ++++++++------ derive/tests/client.rs | 7 +------ 4 files changed, 18 insertions(+), 22 deletions(-) diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index 0912b792f..1887434a7 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -83,8 +83,8 @@ impl RpcMethodAttribute { let aliases = get_meta_list(&meta).map_or(Vec::new(), |ml| get_aliases(ml)); let raw_params = get_meta_list(meta).map_or(false, |ml| has_meta_word(RAW_PARAMS_META_WORD, ml)); - let named_params = - get_meta_list(meta).map_or(false, |ml| has_meta_word(USE_NAMED_PARAMS_META_WORD, ml)); + let named_params = get_meta_list(meta) + .map_or(false, |ml| has_meta_word(USE_NAMED_PARAMS_META_WORD, ml)); Ok(RpcMethodAttribute { attr: attr.clone(), name, @@ -183,7 +183,11 @@ fn validate_attribute_meta(meta: syn::Meta) -> Result { let ident = path_to_str(meta.path()); match ident.as_ref().map(String::as_str) { Some(RPC_ATTR_NAME) => { - validate_idents(&meta, &visitor.meta_words, &[METADATA_META_WORD, RAW_PARAMS_META_WORD, USE_NAMED_PARAMS_META_WORD])?; + validate_idents( + &meta, + &visitor.meta_words, + &[METADATA_META_WORD, RAW_PARAMS_META_WORD, USE_NAMED_PARAMS_META_WORD], + )?; validate_idents(&meta, &visitor.name_value_names, &[RPC_NAME_KEY, RETURNS_META_WORD])?; validate_idents(&meta, &visitor.meta_list_names, &[ALIASES_KEY]) } diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index 171887748..aa7b796de 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -223,9 +223,7 @@ fn rpc_wrapper_mod_name(rpc_trait: &syn::ItemTrait) -> syn::Ident { } fn has_named_params(methods: &[RpcMethod]) -> bool { - methods.iter().any(|method| { - method.attr.named_params - }) + methods.iter().any(|method| method.attr.named_params) } pub fn crate_name(name: &str) -> Result { @@ -263,10 +261,7 @@ pub fn rpc_impl(input: syn::Item, options: DeriveOptions) -> Result Result quote! { // use object style serialization with field names taken from the function param names - serde_json::json!({ - #(stringify!(#arg_names): #arg_names,)* - }) - }, + true => { + quote! { // use object style serialization with field names taken from the function param names + serde_json::json!({ + #(stringify!(#arg_names): #arg_names,)* + }) + } + } false => quote! { // use tuple style serialization (#(#arg_names,)*) - }, + }, }; let client_method = syn::parse_quote! { diff --git a/derive/tests/client.rs b/derive/tests/client.rs index c8fee2a1f..54b1ebfa8 100644 --- a/derive/tests/client.rs +++ b/derive/tests/client.rs @@ -46,10 +46,8 @@ mod client_server { }); tokio::run(fut); } - } - mod named_params { use super::*; use jsonrpc_core::Params; @@ -75,9 +73,7 @@ mod named_params { }); let mut handler = IoHandler::new(); - handler.add_method("call_with_named", |params: Params| { - Ok(params.into()) - }); + handler.add_method("call_with_named", |params: Params| Ok(params.into())); let (client, rpc_client) = local::connect::(handler); let fut = client @@ -94,5 +90,4 @@ mod named_params { }); tokio::run(fut); } - } From 2e266c783d0fd8dfc8ff9011a41f7c506539e7f9 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Mon, 24 Feb 2020 10:16:19 +1100 Subject: [PATCH 08/26] adds ParamStyle enum and conversions. Parses this from meta --- derive/src/lib.rs | 1 + derive/src/params_style.rs | 31 +++++++++++++++++++++++++++++++ derive/src/rpc_attr.rs | 27 +++++++++++++++++++-------- 3 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 derive/src/params_style.rs diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 8fe096eb8..0b7325aff 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -190,6 +190,7 @@ mod rpc_attr; mod rpc_trait; mod to_client; mod to_delegate; +mod params_style; /// Apply `#[rpc]` to a trait, and a `to_delegate` method is generated which /// wires up methods decorated with `#[rpc]` or `#[pubsub]` attributes. diff --git a/derive/src/params_style.rs b/derive/src/params_style.rs new file mode 100644 index 000000000..4a4791055 --- /dev/null +++ b/derive/src/params_style.rs @@ -0,0 +1,31 @@ +use std::str::FromStr; + +const POSITIONAL: &str = "positional"; +const NAMED: &str = "named"; +const RAW: &str = "raw"; + +#[derive(Clone, Debug)] +pub enum ParamStyle { + Positional, + Named, + Raw, +} + +impl Default for ParamStyle { + fn default() -> Self { + Self::Positional + } +} + +impl FromStr for ParamStyle { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + POSITIONAL => Ok(Self::Positional), + NAMED => Ok(Self::Named), + RAW => Ok(Self::Raw), + _ => Err(format!("Invalid value for params key. Must be one of [{}, {}, {}]", POSITIONAL, NAMED, RAW)) + } + } +} diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index 1887434a7..05a060e6c 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -2,6 +2,8 @@ use syn::{ visit::{self, Visit}, Error, Result, }; +use crate::params_style::ParamStyle; +use std::str::FromStr; #[derive(Clone, Debug)] pub struct RpcMethodAttribute { @@ -10,7 +12,7 @@ pub struct RpcMethodAttribute { pub aliases: Vec, pub kind: AttributeKind, pub raw_params: bool, - pub named_params: bool, + pub params_style: ParamStyle, } #[derive(Clone, Debug)] @@ -38,11 +40,11 @@ const SUBSCRIPTION_NAME_KEY: &str = "subscription"; const ALIASES_KEY: &str = "alias"; const PUB_SUB_ATTR_NAME: &str = "pubsub"; const METADATA_META_WORD: &str = "meta"; -const RAW_PARAMS_META_WORD: &str = "raw_params"; -const USE_NAMED_PARAMS_META_WORD: &str = "named_params"; +const RAW_PARAMS_META_WORD: &str = "raw_params"; // to be deprecated in favor of `params = "raw"` const SUBSCRIBE_META_WORD: &str = "subscribe"; const UNSUBSCRIBE_META_WORD: &str = "unsubscribe"; const RETURNS_META_WORD: &str = "returns"; +const PARAMS_STYLE_KEY : &str = "params"; const MULTIPLE_RPC_ATTRIBUTES_ERR: &str = "Expected only a single rpc attribute per method"; const INVALID_ATTR_PARAM_NAMES_ERR: &str = "Invalid attribute parameter(s):"; @@ -83,15 +85,16 @@ impl RpcMethodAttribute { let aliases = get_meta_list(&meta).map_or(Vec::new(), |ml| get_aliases(ml)); let raw_params = get_meta_list(meta).map_or(false, |ml| has_meta_word(RAW_PARAMS_META_WORD, ml)); - let named_params = get_meta_list(meta) - .map_or(false, |ml| has_meta_word(USE_NAMED_PARAMS_META_WORD, ml)); + let params_style = get_meta_list(meta) + .map_or(ParamStyle::default(), |ml| get_params_style(ml).unwrap_or(ParamStyle::default())); + // TODO: Error on invalid value Ok(RpcMethodAttribute { attr: attr.clone(), name, aliases, kind, raw_params, - named_params, + params_style, }) }) }) @@ -186,9 +189,9 @@ fn validate_attribute_meta(meta: syn::Meta) -> Result { validate_idents( &meta, &visitor.meta_words, - &[METADATA_META_WORD, RAW_PARAMS_META_WORD, USE_NAMED_PARAMS_META_WORD], + &[METADATA_META_WORD, RAW_PARAMS_META_WORD], )?; - validate_idents(&meta, &visitor.name_value_names, &[RPC_NAME_KEY, RETURNS_META_WORD])?; + validate_idents(&meta, &visitor.name_value_names, &[RPC_NAME_KEY, RETURNS_META_WORD, PARAMS_STYLE_KEY])?; validate_idents(&meta, &visitor.meta_list_names, &[ALIASES_KEY]) } Some(PUB_SUB_ATTR_NAME) => { @@ -288,6 +291,14 @@ fn get_aliases(ml: &syn::MetaList) -> Vec { }) } +fn get_params_style(ml: &syn::MetaList) -> Result { + get_name_value(PARAMS_STYLE_KEY, ml).map_or(Ok(ParamStyle::default()), |s| { + ParamStyle::from_str(&s).map_err(|e| { + Error::new_spanned(ml, e) + }) + }) +} + fn path_eq_str(path: &syn::Path, s: &str) -> bool { path.get_ident().map_or(false, |i| i == s) } From 7baf3be372f8bfbc4c50d3f00b41cda61032e47d Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Mon, 24 Feb 2020 10:46:00 +1100 Subject: [PATCH 09/26] updates existing tests --- derive/src/params_style.rs | 2 +- derive/src/rpc_attr.rs | 10 +++++----- derive/src/rpc_trait.rs | 5 +++-- derive/src/to_client.rs | 7 ++++--- derive/src/to_delegate.rs | 4 ++-- derive/tests/client.rs | 2 +- derive/tests/ui/attr-invalid-meta-words.stderr | 2 +- derive/tests/ui/attr-invalid-name-values.stderr | 2 +- derive/tests/ui/attr-named-params-on-server.rs | 2 +- derive/tests/ui/attr-named-params-on-server.stderr | 4 ++-- 10 files changed, 21 insertions(+), 19 deletions(-) diff --git a/derive/src/params_style.rs b/derive/src/params_style.rs index 4a4791055..ecf9061c8 100644 --- a/derive/src/params_style.rs +++ b/derive/src/params_style.rs @@ -4,7 +4,7 @@ const POSITIONAL: &str = "positional"; const NAMED: &str = "named"; const RAW: &str = "raw"; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum ParamStyle { Positional, Named, diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index 05a060e6c..7c79a04d8 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -11,7 +11,6 @@ pub struct RpcMethodAttribute { pub name: String, pub aliases: Vec, pub kind: AttributeKind, - pub raw_params: bool, pub params_style: ParamStyle, } @@ -85,15 +84,16 @@ impl RpcMethodAttribute { let aliases = get_meta_list(&meta).map_or(Vec::new(), |ml| get_aliases(ml)); let raw_params = get_meta_list(meta).map_or(false, |ml| has_meta_word(RAW_PARAMS_META_WORD, ml)); - let params_style = get_meta_list(meta) - .map_or(ParamStyle::default(), |ml| get_params_style(ml).unwrap_or(ParamStyle::default())); - // TODO: Error on invalid value + let params_style = match raw_params { + true => ParamStyle::Raw, + false => get_meta_list(meta) + .map_or(ParamStyle::default(), |ml| get_params_style(ml).unwrap_or(ParamStyle::default())) + }; Ok(RpcMethodAttribute { attr: attr.clone(), name, aliases, kind, - raw_params, params_style, }) }) diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index aa7b796de..4acd8ee56 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -2,6 +2,7 @@ use crate::options::DeriveOptions; use crate::rpc_attr::{AttributeKind, PubSubMethodKind, RpcMethodAttribute}; use crate::to_client::generate_client_module; use crate::to_delegate::{generate_trait_item_method, MethodRegistration, RpcMethod}; +use crate::params_style::ParamStyle; use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; use std::collections::HashMap; @@ -22,7 +23,7 @@ const MISSING_UNSUBSCRIBE_METHOD_ERR: &str = e.g. `#[pubsub(subscription = \"hello\", unsubscribe, name = \"hello_unsubscribe\")]`"; const USING_NAMED_PARAMS_WITH_SERVER_ERR: &str = - "The named_params switch can only be used to generate a client (on a trait annotated with #[rpc(client)]). \ + "`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_"; @@ -223,7 +224,7 @@ fn rpc_wrapper_mod_name(rpc_trait: &syn::ItemTrait) -> syn::Ident { } fn has_named_params(methods: &[RpcMethod]) -> bool { - methods.iter().any(|method| method.attr.named_params) + methods.iter().any(|method| method.attr.params_style == ParamStyle::Named) } pub fn crate_name(name: &str) -> Result { diff --git a/derive/src/to_client.rs b/derive/src/to_client.rs index 528e61a50..751f3ee7d 100644 --- a/derive/src/to_client.rs +++ b/derive/src/to_client.rs @@ -1,6 +1,7 @@ use crate::rpc_attr::AttributeKind; use crate::rpc_trait::crate_name; use crate::to_delegate::{generate_where_clause_serialization_predicates, MethodRegistration}; +use crate::params_style::ParamStyle; use proc_macro2::{Ident, TokenStream}; use quote::quote; use syn::punctuated::Punctuated; @@ -101,15 +102,15 @@ fn generate_client_methods(methods: &[MethodRegistration]) -> Result { + let args_serialized = match method.attr.params_style { + ParamStyle::Named => { quote! { // use object style serialization with field names taken from the function param names serde_json::json!({ #(stringify!(#arg_names): #arg_names,)* }) } } - false => quote! { // use tuple style serialization + ParamStyle::Positional | ParamStyle::Raw => quote! { // use tuple style serialization (#(#arg_names,)*) }, }; diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index 81e801628..2764145fa 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -1,6 +1,7 @@ use std::collections::HashSet; use crate::rpc_attr::RpcMethodAttribute; +use crate::params_style::ParamStyle; use quote::quote; use syn::{ parse_quote, @@ -218,7 +219,6 @@ impl RpcMethod { // special args are those which are not passed directly via rpc params: metadata, subscriber let special_args = Self::special_args(¶m_types); param_types.retain(|ty| special_args.iter().find(|(_, sty)| sty == ty).is_none()); - if param_types.len() > TUPLE_FIELD_NAMES.len() { return Err(syn::Error::new_spanned( &self.trait_item, @@ -238,7 +238,7 @@ impl RpcMethod { self.params_with_trailing(trailing_args_num, param_types, tuple_fields) } else if param_types.is_empty() { quote! { let params = params.expect_no_params(); } - } else if self.attr.raw_params { + } else if self.attr.params_style == ParamStyle::Raw { quote! { let params: _jsonrpc_core::Result<_> = Ok((params,)); } } else { quote! { let params = params.parse::<(#(#param_types, )*)>(); } diff --git a/derive/tests/client.rs b/derive/tests/client.rs index 54b1ebfa8..e12bdec19 100644 --- a/derive/tests/client.rs +++ b/derive/tests/client.rs @@ -55,7 +55,7 @@ mod named_params { #[rpc(client)] pub trait Rpc { - #[rpc(name = "call_with_named", named_params)] + #[rpc(name = "call_with_named", params = "named")] fn call_with_named(&self, number: u64, string: String, json: serde_json::Value) -> Result; #[rpc(name = "notify")] diff --git a/derive/tests/ui/attr-invalid-meta-words.stderr b/derive/tests/ui/attr-invalid-meta-words.stderr index d4a8848e5..7b5d63d17 100644 --- a/derive/tests/ui/attr-invalid-meta-words.stderr +++ b/derive/tests/ui/attr-invalid-meta-words.stderr @@ -1,4 +1,4 @@ -error: Invalid attribute parameter(s): 'Xmeta'. Expected 'meta, raw_params, named_params' +error: Invalid attribute parameter(s): 'Xmeta'. Expected 'meta, raw_params' --> $DIR/attr-invalid-meta-words.rs:5:2 | 5 | /// Returns a protocol version diff --git a/derive/tests/ui/attr-invalid-name-values.stderr b/derive/tests/ui/attr-invalid-name-values.stderr index 9bb48a1c7..6811f0c3e 100644 --- a/derive/tests/ui/attr-invalid-name-values.stderr +++ b/derive/tests/ui/attr-invalid-name-values.stderr @@ -1,4 +1,4 @@ -error: Invalid attribute parameter(s): 'Xname'. Expected 'name, returns' +error: Invalid attribute parameter(s): 'Xname'. Expected 'name, returns, params' --> $DIR/attr-invalid-name-values.rs:5:2 | 5 | /// Returns a protocol version diff --git a/derive/tests/ui/attr-named-params-on-server.rs b/derive/tests/ui/attr-named-params-on-server.rs index 761745c8b..074995642 100644 --- a/derive/tests/ui/attr-named-params-on-server.rs +++ b/derive/tests/ui/attr-named-params-on-server.rs @@ -3,7 +3,7 @@ use jsonrpc_derive::rpc; #[rpc] pub trait Rpc { /// Returns a protocol version - #[rpc(name = "add", named_params)] + #[rpc(name = "add", params = "named")] fn add(&self, a: u32, b: u32) -> Result; } diff --git a/derive/tests/ui/attr-named-params-on-server.stderr b/derive/tests/ui/attr-named-params-on-server.stderr index ebb0db78e..41ccc852a 100644 --- a/derive/tests/ui/attr-named-params-on-server.stderr +++ b/derive/tests/ui/attr-named-params-on-server.stderr @@ -1,9 +1,9 @@ -error: The named_params switch 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. +error: `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. --> $DIR/attr-named-params-on-server.rs:4:1 | 4 | / pub trait Rpc { 5 | | /// Returns a protocol version -6 | | #[rpc(name = "add", named_params)] +6 | | #[rpc(name = "add", params = "named")] 7 | | fn add(&self, a: u32, b: u32) -> Result; 8 | | } | |_^ From 097276c65cd86f5c345190372cde46a6127ba455 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Mon, 24 Feb 2020 10:54:07 +1100 Subject: [PATCH 10/26] adds client side support for raw params --- derive/src/lib.rs | 2 +- derive/src/params_style.rs | 7 +++++-- derive/src/rpc_attr.rs | 21 ++++++++++----------- derive/src/rpc_trait.rs | 6 ++++-- derive/src/to_client.rs | 8 ++++++-- derive/src/to_delegate.rs | 2 +- 6 files changed, 27 insertions(+), 19 deletions(-) diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 0b7325aff..071765b59 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -186,11 +186,11 @@ use proc_macro::TokenStream; use syn::parse_macro_input; mod options; +mod params_style; mod rpc_attr; mod rpc_trait; mod to_client; mod to_delegate; -mod params_style; /// Apply `#[rpc]` to a trait, and a `to_delegate` method is generated which /// wires up methods decorated with `#[rpc]` or `#[pubsub]` attributes. diff --git a/derive/src/params_style.rs b/derive/src/params_style.rs index ecf9061c8..fe8b9dfe7 100644 --- a/derive/src/params_style.rs +++ b/derive/src/params_style.rs @@ -20,12 +20,15 @@ impl Default for ParamStyle { impl FromStr for ParamStyle { type Err = String; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { match s { POSITIONAL => Ok(Self::Positional), NAMED => Ok(Self::Named), RAW => Ok(Self::Raw), - _ => Err(format!("Invalid value for params key. Must be one of [{}, {}, {}]", POSITIONAL, NAMED, RAW)) + _ => Err(format!( + "Invalid value for params key. Must be one of [{}, {}, {}]", + POSITIONAL, NAMED, RAW + )), } } } diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index 7c79a04d8..3004bef5e 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -1,9 +1,9 @@ +use crate::params_style::ParamStyle; +use std::str::FromStr; use syn::{ visit::{self, Visit}, Error, Result, }; -use crate::params_style::ParamStyle; -use std::str::FromStr; #[derive(Clone, Debug)] pub struct RpcMethodAttribute { @@ -43,7 +43,7 @@ const RAW_PARAMS_META_WORD: &str = "raw_params"; // to be deprecated in favor of const SUBSCRIBE_META_WORD: &str = "subscribe"; const UNSUBSCRIBE_META_WORD: &str = "unsubscribe"; const RETURNS_META_WORD: &str = "returns"; -const PARAMS_STYLE_KEY : &str = "params"; +const PARAMS_STYLE_KEY: &str = "params"; const MULTIPLE_RPC_ATTRIBUTES_ERR: &str = "Expected only a single rpc attribute per method"; const INVALID_ATTR_PARAM_NAMES_ERR: &str = "Invalid attribute parameter(s):"; @@ -86,8 +86,9 @@ impl RpcMethodAttribute { get_meta_list(meta).map_or(false, |ml| has_meta_word(RAW_PARAMS_META_WORD, ml)); let params_style = match raw_params { true => ParamStyle::Raw, - false => get_meta_list(meta) - .map_or(ParamStyle::default(), |ml| get_params_style(ml).unwrap_or(ParamStyle::default())) + false => get_meta_list(meta).map_or(ParamStyle::default(), |ml| { + get_params_style(ml).unwrap_or(ParamStyle::default()) + }), }; Ok(RpcMethodAttribute { attr: attr.clone(), @@ -186,12 +187,12 @@ fn validate_attribute_meta(meta: syn::Meta) -> Result { let ident = path_to_str(meta.path()); match ident.as_ref().map(String::as_str) { Some(RPC_ATTR_NAME) => { + validate_idents(&meta, &visitor.meta_words, &[METADATA_META_WORD, RAW_PARAMS_META_WORD])?; validate_idents( &meta, - &visitor.meta_words, - &[METADATA_META_WORD, RAW_PARAMS_META_WORD], + &visitor.name_value_names, + &[RPC_NAME_KEY, RETURNS_META_WORD, PARAMS_STYLE_KEY], )?; - validate_idents(&meta, &visitor.name_value_names, &[RPC_NAME_KEY, RETURNS_META_WORD, PARAMS_STYLE_KEY])?; validate_idents(&meta, &visitor.meta_list_names, &[ALIASES_KEY]) } Some(PUB_SUB_ATTR_NAME) => { @@ -293,9 +294,7 @@ fn get_aliases(ml: &syn::MetaList) -> Vec { fn get_params_style(ml: &syn::MetaList) -> Result { get_name_value(PARAMS_STYLE_KEY, ml).map_or(Ok(ParamStyle::default()), |s| { - ParamStyle::from_str(&s).map_err(|e| { - Error::new_spanned(ml, e) - }) + ParamStyle::from_str(&s).map_err(|e| Error::new_spanned(ml, e)) }) } diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index 4acd8ee56..ca7995da1 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -1,8 +1,8 @@ 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}; -use crate::params_style::ParamStyle; use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; use std::collections::HashMap; @@ -224,7 +224,9 @@ fn rpc_wrapper_mod_name(rpc_trait: &syn::ItemTrait) -> syn::Ident { } fn has_named_params(methods: &[RpcMethod]) -> bool { - methods.iter().any(|method| method.attr.params_style == ParamStyle::Named) + methods + .iter() + .any(|method| method.attr.params_style == ParamStyle::Named) } pub fn crate_name(name: &str) -> Result { diff --git a/derive/src/to_client.rs b/derive/src/to_client.rs index 751f3ee7d..825e369c4 100644 --- a/derive/src/to_client.rs +++ b/derive/src/to_client.rs @@ -1,7 +1,7 @@ +use crate::params_style::ParamStyle; use crate::rpc_attr::AttributeKind; use crate::rpc_trait::crate_name; use crate::to_delegate::{generate_where_clause_serialization_predicates, MethodRegistration}; -use crate::params_style::ParamStyle; use proc_macro2::{Ident, TokenStream}; use quote::quote; use syn::punctuated::Punctuated; @@ -110,9 +110,13 @@ fn generate_client_methods(methods: &[MethodRegistration]) -> Result quote! { // use tuple style serialization + ParamStyle::Positional => quote! { // use tuple style serialization (#(#arg_names,)*) }, + ParamStyle::Raw => match arg_names.first() { + Some(arg_name) => quote! {#arg_name}, + None => quote! {serde_json::Value::Null}, + }, }; let client_method = syn::parse_quote! { diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index 2764145fa..f7118b891 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -1,7 +1,7 @@ use std::collections::HashSet; -use crate::rpc_attr::RpcMethodAttribute; use crate::params_style::ParamStyle; +use crate::rpc_attr::RpcMethodAttribute; use quote::quote; use syn::{ parse_quote, From 66488b2e2dc166ec114fdabee885920b755202e5 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Mon, 24 Feb 2020 11:06:53 +1100 Subject: [PATCH 11/26] adds compiler error on invalid params value --- derive/src/rpc_attr.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index 3004bef5e..daa51fb01 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -85,11 +85,11 @@ impl RpcMethodAttribute { let raw_params = get_meta_list(meta).map_or(false, |ml| has_meta_word(RAW_PARAMS_META_WORD, ml)); let params_style = match raw_params { - true => ParamStyle::Raw, - false => get_meta_list(meta).map_or(ParamStyle::default(), |ml| { - get_params_style(ml).unwrap_or(ParamStyle::default()) - }), - }; + true => Ok(ParamStyle::Raw), + false => { + get_meta_list(meta).map_or(Ok(ParamStyle::default()), |ml| get_params_style(ml)) + } + }?; Ok(RpcMethodAttribute { attr: attr.clone(), name, From 95a945cb9fe348f9cb8d2a9d5c00eae0ee4819a4 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Mon, 24 Feb 2020 11:34:28 +1100 Subject: [PATCH 12/26] add client tests --- derive/tests/client.rs | 46 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/derive/tests/client.rs b/derive/tests/client.rs index e12bdec19..a4d585a2a 100644 --- a/derive/tests/client.rs +++ b/derive/tests/client.rs @@ -56,10 +56,10 @@ mod named_params { #[rpc(client)] pub trait Rpc { #[rpc(name = "call_with_named", params = "named")] - fn call_with_named(&self, number: u64, string: String, json: serde_json::Value) -> Result; + fn call_with_named(&self, number: u64, string: String, json: Value) -> Result; #[rpc(name = "notify")] - fn notify(&self, payload: serde_json::Value); + fn notify(&self, payload: Value); } #[test] @@ -91,3 +91,45 @@ mod named_params { tokio::run(fut); } } + +mod raw_params { + use super::*; + use jsonrpc_core::Params; + use serde_json::json; + + #[rpc(client)] + pub trait Rpc { + #[rpc(name = "call_raw", params = "raw")] + fn call_raw_single_param(&self, params: Value) -> Result; + + #[rpc(name = "notify")] + fn notify(&self, payload: Value); + } + + #[test] + fn client_generates_correct_raw_params_payload() { + let expected = json!({ + "sub_object": { + "key": ["value"] + } + }); + + let mut handler = IoHandler::new(); + handler.add_method("call_raw", |params: Params| Ok(params.into())); + + let (client, rpc_client) = local::connect::(handler); + let fut = client + .clone() + .call_raw_single_param(expected.clone()) + .and_then(move |res| client.notify(res.clone()).map(move |_| res)) + .join(rpc_client) + .map(move |(res, ())| { + assert_eq!(res, expected); + }) + .map_err(|err| { + eprintln!("{:?}", err); + assert!(false); + }); + tokio::run(fut); + } +} From 2d2097816c851573f149dff94453a64824f5bc13 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Mon, 24 Feb 2020 13:46:05 +1100 Subject: [PATCH 13/26] adds global switch to trait attribute --- derive/src/lib.rs | 1 + derive/src/options.rs | 60 +++++++++++++++++++++++++++++------------- derive/src/rpc_attr.rs | 4 +-- derive/tests/client.rs | 6 ++--- 4 files changed, 47 insertions(+), 24 deletions(-) diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 071765b59..5a5f42346 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -199,6 +199,7 @@ mod to_delegate; #[proc_macro_attribute] pub fn rpc(args: TokenStream, input: TokenStream) -> TokenStream { let input_toks = parse_macro_input!(input as syn::Item); + let args = syn::parse_macro_input!(args as syn::AttributeArgs); let options = match options::DeriveOptions::try_from(args) { Ok(options) => options, diff --git a/derive/src/options.rs b/derive/src/options.rs index a6ba98ec0..9ec115e3b 100644 --- a/derive/src/options.rs +++ b/derive/src/options.rs @@ -1,37 +1,59 @@ -use proc_macro::TokenStream; +use std::str::FromStr; + +use crate::params_style::ParamStyle; +use crate::rpc_attr::path_eq_str; + +const CLIENT_META_WORD: &str = "client"; +const SERVER_META_WORD: &str = "server"; +const PARAMS_META_KEY: &str = "params"; #[derive(Debug)] pub struct DeriveOptions { pub enable_client: bool, pub enable_server: bool, + pub params_style: ParamStyle, } impl DeriveOptions { - pub fn new(enable_client: bool, enable_server: bool) -> Self { + pub fn new(enable_client: bool, enable_server: bool, params_style: ParamStyle) -> Self { DeriveOptions { enable_client, enable_server, + params_style, } } - pub fn try_from(tokens: TokenStream) -> Result { - if tokens.is_empty() { - return Ok(Self::new(true, true)); - } - let ident: syn::Ident = syn::parse::(tokens)?; - let options = { - let ident = ident.to_string(); - if ident == "client" { - Some(Self::new(true, false)) - } else if ident == "server" { - Some(Self::new(false, true)) - } else { - None + pub fn try_from(args: syn::AttributeArgs) -> Result { + let mut options = DeriveOptions::new(false, false, ParamStyle::default()); + for arg in args { + if let syn::NestedMeta::Meta(meta) = arg { + match meta { + syn::Meta::Path(p) => { + match p.get_ident().unwrap().to_string().as_ref() { + CLIENT_META_WORD => options.enable_client = true, + SERVER_META_WORD => options.enable_server = true, + _ => {} + }; + } + syn::Meta::NameValue(nv) => { + if path_eq_str(&nv.path, PARAMS_META_KEY) { + if let syn::Lit::Str(ref lit) = nv.lit { + options.params_style = ParamStyle::from_str(&lit.value()) + .map_err(|e| syn::Error::new_spanned(nv.clone(), e))?; + } + } else { + return Err(syn::Error::new_spanned(nv, "Unexpected RPC attribute key")); + } + } + _ => return Err(syn::Error::new_spanned(meta, "Unexpected use of RPC attribute macro")), + } } - }; - match options { - Some(options) => Ok(options), - None => Err(syn::Error::new(ident.span(), "Unknown attribute.")), } + if !options.enable_client && !options.enable_server { + // if nothing provided default to both + options.enable_client = true; + options.enable_server = true; + } + Ok(options) } } diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index daa51fb01..c63f07ff5 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -39,7 +39,7 @@ const SUBSCRIPTION_NAME_KEY: &str = "subscription"; const ALIASES_KEY: &str = "alias"; const PUB_SUB_ATTR_NAME: &str = "pubsub"; const METADATA_META_WORD: &str = "meta"; -const RAW_PARAMS_META_WORD: &str = "raw_params"; // to be deprecated in favor of `params = "raw"` +const RAW_PARAMS_META_WORD: &str = "raw_params"; // to be deprecated and replaced with `params = "raw"` const SUBSCRIBE_META_WORD: &str = "subscribe"; const UNSUBSCRIBE_META_WORD: &str = "unsubscribe"; const RETURNS_META_WORD: &str = "returns"; @@ -298,7 +298,7 @@ fn get_params_style(ml: &syn::MetaList) -> Result { }) } -fn path_eq_str(path: &syn::Path, s: &str) -> bool { +pub fn path_eq_str(path: &syn::Path, s: &str) -> bool { path.get_ident().map_or(false, |i| i == s) } diff --git a/derive/tests/client.rs b/derive/tests/client.rs index a4d585a2a..81ee80981 100644 --- a/derive/tests/client.rs +++ b/derive/tests/client.rs @@ -53,12 +53,12 @@ mod named_params { use jsonrpc_core::Params; use serde_json::json; - #[rpc(client)] + #[rpc(client, params = "named")] pub trait Rpc { - #[rpc(name = "call_with_named", params = "named")] + #[rpc(name = "call_with_named")] fn call_with_named(&self, number: u64, string: String, json: Value) -> Result; - #[rpc(name = "notify")] + #[rpc(name = "notify", params = "raw")] fn notify(&self, payload: Value); } From 510048f20790a8ec0cc20349ebcfb6967b6349a1 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Mon, 24 Feb 2020 14:01:45 +1100 Subject: [PATCH 14/26] add logic to override code generation with default from top level --- derive/src/lib.rs | 2 +- derive/src/options.rs | 3 +++ derive/src/rpc_attr.rs | 6 +++--- derive/src/rpc_trait.rs | 6 +++--- derive/src/to_client.rs | 13 +++++++++---- derive/src/to_delegate.rs | 2 +- 6 files changed, 20 insertions(+), 12 deletions(-) diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 5a5f42346..fa8d9fdfe 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -206,7 +206,7 @@ pub fn rpc(args: TokenStream, input: TokenStream) -> TokenStream { Err(error) => return error.to_compile_error().into(), }; - match rpc_trait::rpc_impl(input_toks, options) { + match rpc_trait::rpc_impl(input_toks, &options) { Ok(output) => output.into(), Err(err) => err.to_compile_error().into(), } diff --git a/derive/src/options.rs b/derive/src/options.rs index 9ec115e3b..be6aa713b 100644 --- a/derive/src/options.rs +++ b/derive/src/options.rs @@ -54,6 +54,9 @@ impl DeriveOptions { options.enable_client = true; options.enable_server = true; } + if options.enable_server && options.params_style == ParamStyle::Named { + panic!() + } Ok(options) } } diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index c63f07ff5..631ba93d7 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -11,7 +11,7 @@ pub struct RpcMethodAttribute { pub name: String, pub aliases: Vec, pub kind: AttributeKind, - pub params_style: ParamStyle, + pub params_style: Option, // None means do not overrite the top level default } #[derive(Clone, Debug)] @@ -85,9 +85,9 @@ impl RpcMethodAttribute { let raw_params = get_meta_list(meta).map_or(false, |ml| has_meta_word(RAW_PARAMS_META_WORD, ml)); let params_style = match raw_params { - true => Ok(ParamStyle::Raw), + true => Ok(Some(ParamStyle::Raw)), false => { - get_meta_list(meta).map_or(Ok(ParamStyle::default()), |ml| get_params_style(ml)) + get_meta_list(meta).map_or(Ok(None), |ml| get_params_style(ml).map(|s| Some(s))) } }?; Ok(RpcMethodAttribute { diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index ca7995da1..ccc968589 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -226,7 +226,7 @@ fn rpc_wrapper_mod_name(rpc_trait: &syn::ItemTrait) -> syn::Ident { fn has_named_params(methods: &[RpcMethod]) -> bool { methods .iter() - .any(|method| method.attr.params_style == ParamStyle::Named) + .any(|method| method.attr.params_style == std::option::Option::Some(ParamStyle::Named)) } pub fn crate_name(name: &str) -> Result { @@ -235,7 +235,7 @@ pub fn crate_name(name: &str) -> Result { .map_err(|e| Error::new(Span::call_site(), &e)) } -pub fn rpc_impl(input: syn::Item, options: DeriveOptions) -> Result { +pub fn rpc_impl(input: syn::Item, options: &DeriveOptions) -> Result { let rpc_trait = match input { syn::Item::Trait(item_trait) => item_trait, item => { @@ -256,7 +256,7 @@ pub fn rpc_impl(input: syn::Item, options: DeriveOptions) -> Result Result { - let client_methods = generate_client_methods(methods)?; +pub fn generate_client_module( + methods: &[MethodRegistration], + item_trait: &syn::ItemTrait, + options: &DeriveOptions, +) -> Result { + let client_methods = generate_client_methods(methods, &options)?; let generics = &item_trait.generics; let where_clause = generate_where_clause_serialization_predicates(&item_trait, true); let where_clause2 = where_clause.clone(); @@ -86,7 +91,7 @@ pub fn generate_client_module(methods: &[MethodRegistration], item_trait: &syn:: }) } -fn generate_client_methods(methods: &[MethodRegistration]) -> Result> { +fn generate_client_methods(methods: &[MethodRegistration], options: &DeriveOptions) -> Result> { let mut client_methods = vec![]; for method in methods { match method { @@ -102,7 +107,7 @@ fn generate_client_methods(methods: &[MethodRegistration]) -> Result { quote! { // use object style serialization with field names taken from the function param names serde_json::json!({ diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index f7118b891..a4c57a563 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -238,7 +238,7 @@ impl RpcMethod { self.params_with_trailing(trailing_args_num, param_types, tuple_fields) } else if param_types.is_empty() { quote! { let params = params.expect_no_params(); } - } else if self.attr.params_style == ParamStyle::Raw { + } else if self.attr.params_style == Some(ParamStyle::Raw) { quote! { let params: _jsonrpc_core::Result<_> = Ok((params,)); } } else { quote! { let params = params.parse::<(#(#param_types, )*)>(); } From a6850aebbdbd066f8679c29a2838df0d441fc93a Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Mon, 24 Feb 2020 14:17:49 +1100 Subject: [PATCH 15/26] adds error on changing trait default when generating server with incompatible params type --- derive/src/options.rs | 3 ++- derive/src/rpc_trait.rs | 2 +- derive/tests/client.rs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/derive/src/options.rs b/derive/src/options.rs index be6aa713b..c79117f3e 100644 --- a/derive/src/options.rs +++ b/derive/src/options.rs @@ -55,7 +55,8 @@ impl DeriveOptions { options.enable_server = true; } if options.enable_server && options.params_style == ParamStyle::Named { - panic!() + // This is not allowed at this time + panic!("Server code generation only supports `params = \"positional\"` at this time. This is the default setting.") } Ok(options) } diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index ccc968589..0c54f5c45 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -22,7 +22,7 @@ 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\")]`"; -const USING_NAMED_PARAMS_WITH_SERVER_ERR: &str = +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."; diff --git a/derive/tests/client.rs b/derive/tests/client.rs index 81ee80981..e4cf9412c 100644 --- a/derive/tests/client.rs +++ b/derive/tests/client.rs @@ -6,7 +6,7 @@ use jsonrpc_derive::rpc; mod client_server { use super::*; - #[rpc] + #[rpc(params = "positional")] pub trait Rpc { #[rpc(name = "add")] fn add(&self, a: u64, b: u64) -> Result; From d750f40e8f7245727fadb3eef460d38925c79094 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Mon, 24 Feb 2020 14:21:22 +1100 Subject: [PATCH 16/26] adds tests for preventing server generation with named params --- derive/tests/client.rs | 2 +- derive/tests/ui/trait-attr-named-params-on-server.rs | 7 +++++++ derive/tests/ui/trait-attr-named-params-on-server.stderr | 7 +++++++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 derive/tests/ui/trait-attr-named-params-on-server.rs create mode 100644 derive/tests/ui/trait-attr-named-params-on-server.stderr diff --git a/derive/tests/client.rs b/derive/tests/client.rs index e4cf9412c..f5eff5ee8 100644 --- a/derive/tests/client.rs +++ b/derive/tests/client.rs @@ -102,7 +102,7 @@ mod raw_params { #[rpc(name = "call_raw", params = "raw")] fn call_raw_single_param(&self, params: Value) -> Result; - #[rpc(name = "notify")] + #[rpc(name = "notify", params = "raw")] fn notify(&self, payload: Value); } diff --git a/derive/tests/ui/trait-attr-named-params-on-server.rs b/derive/tests/ui/trait-attr-named-params-on-server.rs new file mode 100644 index 000000000..302768fcf --- /dev/null +++ b/derive/tests/ui/trait-attr-named-params-on-server.rs @@ -0,0 +1,7 @@ +use jsonrpc_derive::rpc; + +#[rpc(server, params = "named")] +pub trait Rpc { +} + +fn main() {} diff --git a/derive/tests/ui/trait-attr-named-params-on-server.stderr b/derive/tests/ui/trait-attr-named-params-on-server.stderr new file mode 100644 index 000000000..21e6be80a --- /dev/null +++ b/derive/tests/ui/trait-attr-named-params-on-server.stderr @@ -0,0 +1,7 @@ +error: custom attribute panicked + --> $DIR/trait-attr-named-params-on-server.rs:3:1 + | +3 | #[rpc(server, params = "named")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: Server code generation only supports `params = "positional"` at this time. This is the default setting. From 36bab4e1c98630604477b4b63b540d6675d17a83 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Mon, 24 Feb 2020 13:27:37 +1000 Subject: [PATCH 17/26] Update derive/src/rpc_attr.rs --- derive/src/rpc_attr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index 631ba93d7..3af0529c5 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -11,7 +11,7 @@ pub struct RpcMethodAttribute { pub name: String, pub aliases: Vec, pub kind: AttributeKind, - pub params_style: Option, // None means do not overrite the top level default + pub params_style: Option, // None means do not override the top level default } #[derive(Clone, Debug)] From 4721ce2e9ed5901908e80f10c8f6f01ed4eb3238 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Fri, 28 Feb 2020 08:38:05 +1000 Subject: [PATCH 18/26] Update derive/src/rpc_trait.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Tomasz DrwiÄ™ga --- derive/src/rpc_trait.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/derive/src/rpc_trait.rs b/derive/src/rpc_trait.rs index 0c54f5c45..7550ade4f 100644 --- a/derive/src/rpc_trait.rs +++ b/derive/src/rpc_trait.rs @@ -226,7 +226,7 @@ fn rpc_wrapper_mod_name(rpc_trait: &syn::ItemTrait) -> syn::Ident { fn has_named_params(methods: &[RpcMethod]) -> bool { methods .iter() - .any(|method| method.attr.params_style == std::option::Option::Some(ParamStyle::Named)) + .any(|method| method.attr.params_style == Some(ParamStyle::Named)) } pub fn crate_name(name: &str) -> Result { From e7abc093584167beb66924578f8d04a7348b5e51 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Tue, 3 Mar 2020 15:39:53 +1100 Subject: [PATCH 19/26] remove unwrap and throw useful compile time error --- derive/src/options.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/derive/src/options.rs b/derive/src/options.rs index c79117f3e..e6ae47748 100644 --- a/derive/src/options.rs +++ b/derive/src/options.rs @@ -28,8 +28,16 @@ impl DeriveOptions { for arg in args { if let syn::NestedMeta::Meta(meta) = arg { match meta { - syn::Meta::Path(p) => { - match p.get_ident().unwrap().to_string().as_ref() { + syn::Meta::Path(ref p) => { + match p + .get_ident() + .ok_or(syn::Error::new_spanned( + p, + format!("Expecting identifier `{}` or `{}`", CLIENT_META_WORD, SERVER_META_WORD), + ))? + .to_string() + .as_ref() + { CLIENT_META_WORD => options.enable_client = true, SERVER_META_WORD => options.enable_server = true, _ => {} From 38bb7f04e7efd82fd34794a18315c2ee38cee4d1 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Tue, 3 Mar 2020 15:44:02 +1100 Subject: [PATCH 20/26] fixes error, notifies that server also supports raw params --- derive/src/options.rs | 2 +- derive/tests/ui/trait-attr-named-params-on-server.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/derive/src/options.rs b/derive/src/options.rs index e6ae47748..fe52c07dc 100644 --- a/derive/src/options.rs +++ b/derive/src/options.rs @@ -64,7 +64,7 @@ impl DeriveOptions { } if options.enable_server && options.params_style == ParamStyle::Named { // This is not allowed at this time - panic!("Server code generation only supports `params = \"positional\"` at this time. This is the default setting.") + panic!("Server code generation only supports `params = \"positional\"` (default) or `params = \"raw\" at this time.") } Ok(options) } diff --git a/derive/tests/ui/trait-attr-named-params-on-server.stderr b/derive/tests/ui/trait-attr-named-params-on-server.stderr index 21e6be80a..c44d44465 100644 --- a/derive/tests/ui/trait-attr-named-params-on-server.stderr +++ b/derive/tests/ui/trait-attr-named-params-on-server.stderr @@ -4,4 +4,4 @@ error: custom attribute panicked 3 | #[rpc(server, params = "named")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: message: Server code generation only supports `params = "positional"` at this time. This is the default setting. + = help: message: Server code generation only supports `params = "positional"` (default) or `params = "raw" at this time. From 0d4bb3ce119a7237755d95d4f6ec500059dd273a Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Tue, 3 Mar 2020 16:00:35 +1100 Subject: [PATCH 21/26] add placeholder for future support for named params server side --- derive/src/to_delegate.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index a4c57a563..241c36792 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -240,8 +240,10 @@ impl RpcMethod { quote! { let params = params.expect_no_params(); } } else if self.attr.params_style == Some(ParamStyle::Raw) { quote! { let params: _jsonrpc_core::Result<_> = Ok((params,)); } - } else { + } else if self.attr.params_style == Some(ParamStyle::Positional) { quote! { let params = params.parse::<(#(#param_types, )*)>(); } + } else /* if self.attr.params_style == Some(ParamStyle::Named) */ { + unimplemented!("Server side named parameters are not implemented"); } }; From 82257ea069b0ee9c88d6d41008f3738d5efc98c8 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Tue, 3 Mar 2020 16:06:06 +1100 Subject: [PATCH 22/26] remove raw_params from examples and replace with params = raw --- derive/examples/meta-macros.rs | 2 +- derive/tests/macros.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/derive/examples/meta-macros.rs b/derive/examples/meta-macros.rs index 58164e31a..8d7a64d7f 100644 --- a/derive/examples/meta-macros.rs +++ b/derive/examples/meta-macros.rs @@ -26,7 +26,7 @@ pub trait Rpc { fn mul(&self, a: u64, b: Option) -> Result; /// Retrieves and debug prints the underlying `Params` object. - #[rpc(name = "raw", raw_params)] + #[rpc(name = "raw", params = "raw")] fn raw(&self, params: Params) -> Result; /// Performs an asynchronous operation. diff --git a/derive/tests/macros.rs b/derive/tests/macros.rs index 632c27a8c..1f4483672 100644 --- a/derive/tests/macros.rs +++ b/derive/tests/macros.rs @@ -27,7 +27,7 @@ pub trait Rpc { fn add(&self, a: u64, b: u64) -> Result; /// Retrieves and debug prints the underlying `Params` object. - #[rpc(name = "raw", raw_params)] + #[rpc(name = "raw", params = "raw")] fn raw(&self, params: Params) -> Result; /// Handles a notification. From 193399352ba1478e9dca0d0ac5f6df3225dd709a Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Tue, 3 Mar 2020 16:08:54 +1100 Subject: [PATCH 23/26] run cargo fmt --- derive/src/to_delegate.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/derive/src/to_delegate.rs b/derive/src/to_delegate.rs index 241c36792..80c83ab31 100644 --- a/derive/src/to_delegate.rs +++ b/derive/src/to_delegate.rs @@ -242,7 +242,9 @@ impl RpcMethod { quote! { let params: _jsonrpc_core::Result<_> = Ok((params,)); } } else if self.attr.params_style == Some(ParamStyle::Positional) { quote! { let params = params.parse::<(#(#param_types, )*)>(); } - } else /* if self.attr.params_style == Some(ParamStyle::Named) */ { + } else + /* if self.attr.params_style == Some(ParamStyle::Named) */ + { unimplemented!("Server side named parameters are not implemented"); } }; From e7e0cd5704c6287124aca25168f7a6fbd0d2e936 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Thu, 5 Mar 2020 14:18:45 +1100 Subject: [PATCH 24/26] add deprecation message placeholder --- derive/src/rpc_attr.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/derive/src/rpc_attr.rs b/derive/src/rpc_attr.rs index 3af0529c5..215979d19 100644 --- a/derive/src/rpc_attr.rs +++ b/derive/src/rpc_attr.rs @@ -85,7 +85,10 @@ impl RpcMethodAttribute { let raw_params = get_meta_list(meta).map_or(false, |ml| has_meta_word(RAW_PARAMS_META_WORD, ml)); let params_style = match raw_params { - true => Ok(Some(ParamStyle::Raw)), + true => { + // "`raw_params` will be deprecated in a future release. Use `params = \"raw\" instead`" + Ok(Some(ParamStyle::Raw)) + } false => { get_meta_list(meta).map_or(Ok(None), |ml| get_params_style(ml).map(|s| Some(s))) } From 2c69f44b26f301433358dc6ee74ba0300b28e2b4 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Sun, 15 Mar 2020 11:14:17 +1100 Subject: [PATCH 25/26] bump and lock quote version --- derive/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 71e6bb35e..1a1febc5d 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -15,7 +15,7 @@ proc-macro = true [dependencies] syn = { version = "1.0", features = ["full", "extra-traits", "visit", "fold"] } proc-macro2 = "1.0" -quote = "1.0" +quote = "=1.0.2" proc-macro-crate = "0.1.4" [dev-dependencies] From 15a5b8b178e28a80aef3c41262dfb7d71d189506 Mon Sep 17 00:00:00 2001 From: Willem Olding Date: Mon, 23 Mar 2020 22:07:08 +1100 Subject: [PATCH 26/26] rollback quote to 1.0.1 --- derive/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 1a1febc5d..8199f796a 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -15,7 +15,7 @@ proc-macro = true [dependencies] syn = { version = "1.0", features = ["full", "extra-traits", "visit", "fold"] } proc-macro2 = "1.0" -quote = "=1.0.2" +quote = "=1.0.1" proc-macro-crate = "0.1.4" [dev-dependencies]