Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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.1"
proc-macro-crate = "0.1.4"

[dev-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion derive/examples/meta-macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub trait Rpc<One> {
fn mul(&self, a: u64, b: Option<u64>) -> Result<u64>;

/// Retrieves and debug prints the underlying `Params` object.
#[rpc(name = "raw", raw_params)]
#[rpc(name = "raw", params = "raw")]
fn raw(&self, params: Params) -> Result<String>;

/// Performs an asynchronous operation.
Expand Down
4 changes: 3 additions & 1 deletion derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ use proc_macro::TokenStream;
use syn::parse_macro_input;

mod options;
mod params_style;
mod rpc_attr;
mod rpc_trait;
mod to_client;
Expand All @@ -198,13 +199,14 @@ 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,
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(),
}
Expand Down
72 changes: 53 additions & 19 deletions derive/src/options.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,71 @@
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<Self, syn::Error> {
if tokens.is_empty() {
return Ok(Self::new(true, true));
}
let ident: syn::Ident = syn::parse::<syn::Ident>(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<Self, syn::Error> {
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(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,
_ => {}
};
}
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;
}
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)
}
}
34 changes: 34 additions & 0 deletions derive/src/params_style.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use std::str::FromStr;

const POSITIONAL: &str = "positional";
const NAMED: &str = "named";
const RAW: &str = "raw";

#[derive(Clone, Debug, PartialEq)]
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<Self, String> {
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
)),
}
}
}
32 changes: 27 additions & 5 deletions derive/src/rpc_attr.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::params_style::ParamStyle;
use std::str::FromStr;
use syn::{
visit::{self, Visit},
Error, Result,
Expand All @@ -9,7 +11,7 @@ pub struct RpcMethodAttribute {
pub name: String,
pub aliases: Vec<String>,
pub kind: AttributeKind,
pub raw_params: bool,
pub params_style: Option<ParamStyle>, // None means do not override the top level default
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -37,10 +39,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 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";
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):";
Expand Down Expand Up @@ -81,12 +84,21 @@ 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 = match raw_params {
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)))
}
}?;
Ok(RpcMethodAttribute {
attr: attr.clone(),
name,
aliases,
kind,
raw_params,
params_style,
})
})
})
Expand Down Expand Up @@ -179,7 +191,11 @@ fn validate_attribute_meta(meta: syn::Meta) -> Result<syn::Meta> {
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.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) => {
Expand Down Expand Up @@ -279,7 +295,13 @@ fn get_aliases(ml: &syn::MetaList) -> Vec<String> {
})
}

fn path_eq_str(path: &syn::Path, s: &str) -> bool {
fn get_params_style(ml: &syn::MetaList) -> Result<ParamStyle> {
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))
})
}

pub fn path_eq_str(path: &syn::Path, s: &str) -> bool {
path.get_ident().map_or(false, |i| i == s)
}

Expand Down
18 changes: 16 additions & 2 deletions derive/src/rpc_trait.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
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 @@ -21,6 +22,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\")]`";

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 @@ -218,13 +223,19 @@ 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()))
.map_err(|e| Error::new(Span::call_site(), &e))
}

pub fn rpc_impl(input: syn::Item, options: DeriveOptions) -> Result<proc_macro2::TokenStream> {
pub fn rpc_impl(input: syn::Item, options: &DeriveOptions) -> Result<proc_macro2::TokenStream> {
let rpc_trait = match input {
syn::Item::Trait(item_trait) => item_trait,
item => {
Expand All @@ -245,13 +256,16 @@ pub fn rpc_impl(input: syn::Item, options: DeriveOptions) -> Result<proc_macro2:
let mut submodules = Vec::new();
let mut exports = Vec::new();
if options.enable_client {
let rpc_client_module = generate_client_module(&method_registrations, &rpc_trait)?;
let rpc_client_module = generate_client_module(&method_registrations, &rpc_trait, options)?;
submodules.push(rpc_client_module);
exports.push(quote! {
pub use self::#mod_name_ident::gen_client;
});
}
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
34 changes: 29 additions & 5 deletions derive/src/to_client.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::options::DeriveOptions;
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};
Expand All @@ -6,8 +8,12 @@ use quote::quote;
use syn::punctuated::Punctuated;
use syn::Result;

pub fn generate_client_module(methods: &[MethodRegistration], item_trait: &syn::ItemTrait) -> Result<TokenStream> {
let client_methods = generate_client_methods(methods)?;
pub fn generate_client_module(
methods: &[MethodRegistration],
item_trait: &syn::ItemTrait,
options: &DeriveOptions,
) -> Result<TokenStream> {
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();
Expand Down Expand Up @@ -85,7 +91,7 @@ pub fn generate_client_module(methods: &[MethodRegistration], item_trait: &syn::
})
}

fn generate_client_methods(methods: &[MethodRegistration]) -> Result<Vec<syn::ImplItem>> {
fn generate_client_methods(methods: &[MethodRegistration], options: &DeriveOptions) -> Result<Vec<syn::ImplItem>> {
let mut client_methods = vec![];
for method in methods {
match method {
Expand All @@ -100,11 +106,29 @@ 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.params_style.clone().unwrap_or(options.params_style.clone()) {
ParamStyle::Named => {
quote! { // use object style serialization with field names taken from the function param names
serde_json::json!({
#(stringify!(#arg_names): #arg_names,)*
})
}
}
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! {
#(#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
Loading