Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4bf380d
add support for json objects in TypedClient.call_method
willemolding Feb 17, 2020
c65efae
adds named params switch to #[rpc] method annotation
willemolding Feb 17, 2020
187078a
add generating of map style params in macro
willemolding Feb 17, 2020
167612f
prevent creating a server when using the named_params switch
willemolding Feb 18, 2020
4cfa00b
add test for attempt to derive with named_params on server. refactors…
willemolding Feb 18, 2020
f158a7f
adds test that correct params object is generated
willemolding Feb 18, 2020
b0fefbc
apply cargo fmt
willemolding Feb 18, 2020
2e266c7
adds ParamStyle enum and conversions. Parses this from meta
willemolding Feb 23, 2020
7baf3be
updates existing tests
willemolding Feb 23, 2020
097276c
adds client side support for raw params
willemolding Feb 23, 2020
66488b2
adds compiler error on invalid params value
willemolding Feb 24, 2020
95a945c
add client tests
willemolding Feb 24, 2020
2d20978
adds global switch to trait attribute
willemolding Feb 24, 2020
510048f
add logic to override code generation with default from top level
willemolding Feb 24, 2020
a6850ae
adds error on changing trait default when generating server with inco…
willemolding Feb 24, 2020
d750f40
adds tests for preventing server generation with named params
willemolding Feb 24, 2020
36bab4e
Update derive/src/rpc_attr.rs
willemolding Feb 24, 2020
4721ce2
Update derive/src/rpc_trait.rs
willemolding Feb 27, 2020
e7abc09
remove unwrap and throw useful compile time error
willemolding Mar 3, 2020
38bb7f0
fixes error, notifies that server also supports raw params
willemolding Mar 3, 2020
0d4bb3c
add placeholder for future support for named params server side
willemolding Mar 3, 2020
82257ea
remove raw_params from examples and replace with params = raw
willemolding Mar 3, 2020
1933993
run cargo fmt
willemolding Mar 3, 2020
e7e0cd5
add deprecation message placeholder
willemolding Mar 5, 2020
2c69f44
bump and lock quote version
willemolding Mar 15, 2020
15a5b8b
rollback quote to 1.0.1
willemolding Mar 23, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion core-client/transports/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"
))))
}
};
Expand Down
11 changes: 10 additions & 1 deletion derive/src/rpc_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub struct RpcMethodAttribute {
pub aliases: Vec<String>,
pub kind: AttributeKind,
pub raw_params: bool,
pub named_params: bool,
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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,
})
})
})
Expand Down Expand Up @@ -178,7 +183,11 @@ fn validate_attribute_meta(meta: syn::Meta) -> Result<syn::Meta> {
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])
}
Expand Down
11 changes: 11 additions & 0 deletions derive/src/rpc_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -218,6 +222,10 @@ 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<Ident> {
proc_macro_crate::crate_name(name)
.map(|name| Ident::new(&name, Span::call_site()))
Expand Down Expand Up @@ -252,6 +260,9 @@ 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
18 changes: 16 additions & 2 deletions derive/src/to_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,25 @@ fn generate_client_methods(methods: &[MethodRegistration]) -> Result<Vec<syn::Im
AttributeKind::PubSub { .. } => 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<Item=#returns, Error=RpcError> {
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);
Expand Down
108 changes: 78 additions & 30 deletions derive/tests/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,91 @@ 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<u64>;
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<u64>;

#[rpc(name = "notify")]
fn notify(&self, foo: u64);
}

struct RpcServer;
struct RpcServer;

impl Rpc for RpcServer {
fn add(&self, a: u64, b: u64) -> Result<u64> {
Ok(a + b)
impl Rpc for RpcServer {
fn add(&self, a: u64, b: u64) -> Result<u64> {
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::<gen_client::Client, _, _>(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::<gen_client::Client, _, _>(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)]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to specify named_params in the trait level attribute instead? On the one hand it's good to have the fine grained control - but in practice wouldn't all methods be either named or positional?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have raw_params on the method level. I think this is a good idea to have it here, but an additional attribute on the trait level would be cool tool, especially in the future if we implement server side as well.

fn call_with_named(&self, number: u64, string: String, json: serde_json::Value) -> Result<serde_json::Value>;

#[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"]
}
});
tokio::run(fut);

let mut handler = IoHandler::new();
handler.add_method("call_with_named", |params: Params| Ok(params.into()));

let (client, rpc_client) = local::connect::<gen_client::Client, _, _>(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);
}
}
2 changes: 1 addition & 1 deletion derive/tests/ui/attr-invalid-meta-words.stderr
Original file line number Diff line number Diff line change
@@ -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
Expand Down
10 changes: 10 additions & 0 deletions derive/tests/ui/attr-named-params-on-server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use jsonrpc_derive::rpc;

#[rpc]
pub trait Rpc {
/// Returns a protocol version
#[rpc(name = "add", named_params)]
fn add(&self, a: u32, b: u32) -> Result<String>;
}

fn main() {}
9 changes: 9 additions & 0 deletions derive/tests/ui/attr-named-params-on-server.stderr
Original file line number Diff line number Diff line change
@@ -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<String>;
8 | | }
| |_^