Unverified Commit bc11e9de authored by Niklas Adolfsson's avatar Niklas Adolfsson Committed by GitHub
Browse files

proc macro: support camelCase & snake_case for object params (#921)

* proc macro: support camelCase & snake_case for object params

* switch to heck for converting case

* fix ui test
parent c4fafffd
Pipeline #224765 failed with stages
in 5 minutes and 43 seconds
......@@ -17,6 +17,7 @@ proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "1.0", default-features = false, features = ["extra-traits", "full", "visit", "parsing"] }
proc-macro-crate = "1"
heck = "0.4.0"
[dev-dependencies]
jsonrpsee = { path = "../jsonrpsee", features = ["full"] }
......
......@@ -25,6 +25,7 @@
// DEALINGS IN THE SOFTWARE.
use std::collections::HashSet;
use std::str::FromStr;
use super::RpcDescription;
use crate::attributes::Resource;
......@@ -32,7 +33,7 @@ use crate::helpers::{generate_where_clause, is_option};
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{quote, quote_spanned};
use syn::punctuated::Punctuated;
use syn::{parse_quote, ReturnType, Token};
use syn::{parse_quote, token, AttrStyle, Attribute, Path, PathSegment, ReturnType, Token};
impl RpcDescription {
pub(super) fn render_server(&self) -> Result<TokenStream2, syn::Error> {
......@@ -387,8 +388,36 @@ impl RpcDescription {
let serde = self.jrps_server_item(quote! { core::__reexports::serde });
let serde_crate = serde.to_string();
let fields = params.iter().zip(generics.clone()).map(|((name, _), ty)| {
quote! { #name: #ty, }
let mut alias_vals = String::new();
alias_vals.push_str(&format!(
r#"alias = "{}""#,
heck::ToSnakeCase::to_snake_case(name.ident.to_string().as_str())
));
alias_vals.push(',');
alias_vals.push_str(&format!(
r#"alias = "{}""#,
heck::ToLowerCamelCase::to_lower_camel_case(name.ident.to_string().as_str())
));
let mut punc_attr = Punctuated::new();
punc_attr
.push_value(PathSegment { ident: quote::format_ident!("serde"), arguments: Default::default() });
let serde_alias = Attribute {
pound_token: token::Pound::default(),
style: AttrStyle::Outer,
bracket_token: Default::default(),
path: Path { leading_colon: None, segments: punc_attr },
tokens: TokenStream2::from_str(&format!("({})", alias_vals.as_str())).unwrap(),
};
quote! {
#serde_alias
#name: #ty,
}
});
let destruct = params.iter().map(|(name, _)| quote! { parsed.#name });
let types = params.iter().map(|(_, ty)| ty);
......
error[E0277]: the trait bound `<Conf as Config>::Hash: Serialize` is not satisfied
--> tests/ui/incorrect/rpc/rpc_empty_bounds.rs:10:1
|
10 | #[rpc(server, client, namespace = "foo", client_bounds(), server_bounds())]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Serialize` is not implemented for `<Conf as Config>::Hash`
|
--> tests/ui/incorrect/rpc/rpc_empty_bounds.rs:10:1
|
10 | #[rpc(server, client, namespace = "foo", client_bounds(), server_bounds())]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Serialize` is not implemented for `<Conf as Config>::Hash`
|
note: required by a bound in `RpcModule::<Context>::register_method`
--> $WORKSPACE/core/src/server/rpc_module.rs
|
| R: Serialize,
| ^^^^^^^^^ required by this bound in `RpcModule::<Context>::register_method`
= note: this error originates in the attribute macro `rpc` (in Nightly builds, run with -Z macro-backtrace for more info)
--> $WORKSPACE/core/src/server/rpc_module.rs
|
| R: Serialize,
| ^^^^^^^^^ required by this bound in `RpcModule::<Context>::register_method`
= note: this error originates in the attribute macro `rpc` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `for<'de> <Conf as Config>::Hash: Deserialize<'de>` is not satisfied
--> tests/ui/incorrect/rpc/rpc_empty_bounds.rs:10:1
......@@ -17,7 +17,7 @@ error[E0277]: the trait bound `for<'de> <Conf as Config>::Hash: Deserialize<'de>
10 | #[rpc(server, client, namespace = "foo", client_bounds(), server_bounds())]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'de> Deserialize<'de>` is not implemented for `<Conf as Config>::Hash`
|
= note: required because of the requirements on the impl of `DeserializeOwned` for `<Conf as Config>::Hash`
= note: required for `<Conf as Config>::Hash` to implement `DeserializeOwned`
note: required by a bound in `request`
--> $WORKSPACE/core/src/client/mod.rs
|
......
......@@ -375,3 +375,26 @@ async fn calls_with_bad_params() {
matches!(err, Error::Call(CallError::Custom (err)) if err.message().contains("invalid type: integer `2`, expected a string") && err.code() == ErrorCode::InvalidParams.code())
);
}
#[tokio::test]
async fn calls_with_object_params_works() {
let server = ServerBuilder::default().build("127.0.0.1:0").await.unwrap();
let addr = server.local_addr().unwrap();
let server_url = format!("ws://{}", addr);
let _handle = server.start(RpcServerImpl.into_rpc()).unwrap();
let client = WsClientBuilder::default().build(&server_url).await.unwrap();
// snake_case params
let mut params = ObjectParams::new();
params.insert("param_a", 0).unwrap();
params.insert("param_b", "0x0").unwrap();
assert_eq!(client.request::<u64, ObjectParams>("foo_foo", params).await.unwrap(), 42);
// camelCase params.
let mut params = ObjectParams::new();
params.insert("paramA", 0).unwrap();
params.insert("paramB", "0x0").unwrap();
assert_eq!(client.request::<u64, ObjectParams>("foo_foo", params).await.unwrap(), 42);
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment