From 598f439b5e221baea18f03d26225c9fc610fa9d4 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Thu, 23 Jun 2022 16:22:36 +0300 Subject: [PATCH 01/12] Parse user defined client_bounds and server_bounds Signed-off-by: Alexandru Vasile --- proc-macros/src/rpc_macro.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/proc-macros/src/rpc_macro.rs b/proc-macros/src/rpc_macro.rs index c12e43bf35..0bd9eb0e25 100644 --- a/proc-macros/src/rpc_macro.rs +++ b/proc-macros/src/rpc_macro.rs @@ -220,15 +220,22 @@ pub struct RpcDescription { pub(crate) methods: Vec, /// List of RPC subscriptions defined in the trait. pub(crate) subscriptions: Vec, + /// Optional user defined trait bounds for the client implementation. + pub(crate) client_bounds: Option>, + /// Optional user defined trait bounds for the server implementation. + pub(crate) server_bounds: Option>, } impl RpcDescription { pub fn from_item(attr: Attribute, mut item: syn::ItemTrait) -> syn::Result { - let [client, server, namespace] = AttributeMeta::parse(attr)?.retain(["client", "server", "namespace"])?; + let [client, server, namespace, client_bounds, server_bounds] = + AttributeMeta::parse(attr)?.retain(["client", "server", "namespace", "client_bounds", "server_bounds"])?; let needs_server = optional(server, Argument::flag)?.is_some(); let needs_client = optional(client, Argument::flag)?.is_some(); let namespace = optional(namespace, Argument::string)?; + let client_bounds = optional(client_bounds, Argument::group::)?; + let server_bounds = optional(server_bounds, Argument::group::)?; if !needs_server && !needs_client { return Err(syn::Error::new_spanned(&item.ident, "Either 'server' or 'client' attribute must be applied")); @@ -313,6 +320,8 @@ impl RpcDescription { trait_def: item, methods, subscriptions, + client_bounds, + server_bounds, }) } From ae251b5e327c0f88459ce8c5cceb7b73a48ed993 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Thu, 23 Jun 2022 19:36:59 +0300 Subject: [PATCH 02/12] Use custom user defined bounds if provided Signed-off-by: Alexandru Vasile --- proc-macros/src/helpers.rs | 7 ++++++- proc-macros/src/render_client.rs | 2 +- proc-macros/src/render_server.rs | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/proc-macros/src/helpers.rs b/proc-macros/src/helpers.rs index 0c4f1991ed..275c6220cf 100644 --- a/proc-macros/src/helpers.rs +++ b/proc-macros/src/helpers.rs @@ -30,7 +30,7 @@ use crate::visitor::{FindAllParams, FindSubscriptionParams}; use proc_macro2::{Span, TokenStream as TokenStream2}; use proc_macro_crate::{crate_name, FoundCrate}; use quote::quote; -use syn::{parse_quote, punctuated::Punctuated, visit::Visit, Token}; +use syn::{parse_quote, punctuated::Punctuated, token::Comma, visit::Visit, Token, WherePredicate}; /// Search for client-side `jsonrpsee` in `Cargo.toml`. pub(crate) fn find_jsonrpsee_client_crate() -> Result { @@ -91,10 +91,15 @@ pub(crate) fn generate_where_clause( item_trait: &syn::ItemTrait, sub_tys: &[syn::Type], is_client: bool, + bounds: Option<&Punctuated>, ) -> Vec { let visitor = visit_trait(item_trait, sub_tys); let additional_where_clause = item_trait.generics.where_clause.clone(); + if let Some(custom_bounds) = bounds { + return custom_bounds.iter().cloned().collect(); + } + item_trait .generics .type_params() diff --git a/proc-macros/src/render_client.rs b/proc-macros/src/render_client.rs index 39e077687a..db70c415dc 100644 --- a/proc-macros/src/render_client.rs +++ b/proc-macros/src/render_client.rs @@ -36,7 +36,7 @@ impl RpcDescription { let sub_tys: Vec = self.subscriptions.clone().into_iter().map(|s| s.item).collect(); let trait_name = quote::format_ident!("{}Client", &self.trait_def.ident); - let where_clause = generate_where_clause(&self.trait_def, &sub_tys, true); + let where_clause = generate_where_clause(&self.trait_def, &sub_tys, true, self.client_bounds.as_ref()); let type_idents = self.trait_def.generics.type_params().collect::>(); let (impl_generics, type_generics, _) = self.trait_def.generics.split_for_impl(); diff --git a/proc-macros/src/render_server.rs b/proc-macros/src/render_server.rs index 2f7807f66c..a5e9ea5877 100644 --- a/proc-macros/src/render_server.rs +++ b/proc-macros/src/render_server.rs @@ -281,7 +281,7 @@ impl RpcDescription { and adds them into a single `RpcModule`."; let sub_tys: Vec = self.subscriptions.clone().into_iter().map(|s| s.item).collect(); - let where_clause = generate_where_clause(&self.trait_def, &sub_tys, false); + let where_clause = generate_where_clause(&self.trait_def, &sub_tys, false, self.server_bounds.as_ref()); // NOTE(niklasad1): empty where clause is valid rust syntax. Ok(quote! { From 7aa178f4d97dbce8e0d9e17fcbf9407f029681a1 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 24 Jun 2022 11:42:11 +0300 Subject: [PATCH 03/12] Add provided where clause to the custom bounds Signed-off-by: Alexandru Vasile --- proc-macros/src/helpers.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/proc-macros/src/helpers.rs b/proc-macros/src/helpers.rs index 275c6220cf..cf6c7b92b7 100644 --- a/proc-macros/src/helpers.rs +++ b/proc-macros/src/helpers.rs @@ -97,7 +97,13 @@ pub(crate) fn generate_where_clause( let additional_where_clause = item_trait.generics.where_clause.clone(); if let Some(custom_bounds) = bounds { - return custom_bounds.iter().cloned().collect(); + let mut bounds = additional_where_clause + .map(|where_clause| where_clause.predicates.into_iter().collect()) + .unwrap_or(Vec::new()); + + bounds.extend(custom_bounds.iter().cloned()); + + return bounds; } item_trait From 77a6911e9a8742c25a165590758a983da8c1fae3 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 24 Jun 2022 14:45:08 +0300 Subject: [PATCH 04/12] Add proc_macro with bounds example Signed-off-by: Alexandru Vasile --- examples/examples/proc_macro_bounds.rs | 90 ++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 examples/examples/proc_macro_bounds.rs diff --git a/examples/examples/proc_macro_bounds.rs b/examples/examples/proc_macro_bounds.rs new file mode 100644 index 0000000000..1f77991d47 --- /dev/null +++ b/examples/examples/proc_macro_bounds.rs @@ -0,0 +1,90 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use std::net::SocketAddr; + +use jsonrpsee::core::{async_trait, Error}; +use jsonrpsee::proc_macros::rpc; +use jsonrpsee::ws_client::WsClientBuilder; +use jsonrpsee::ws_server::{WsServerBuilder, WsServerHandle}; + +type ExampleHash = [u8; 32]; + +pub trait Config { + type Hash: Send + Sync + 'static; +} + +impl Config for ExampleHash { + type Hash = Self; +} + +/// The RPC macro requires `DeserializeOwned` for output types for the client implementation, while the +/// server implementation requires output types to be bounded by `Serialize`. +/// +/// In this example, we don't want the `Conf` to be bounded by default to +/// `Conf : Send + Sync + 'static + jsonrpsee::core::DeserializeOwned` for client implementation and +/// `Conf : Send + Sync + 'static + jsonrpsee::core::Serialize` for server implementation. +/// +/// Explicitly, specify client and server bounds to handle the `Serialize` and `DeserializeOwned` cases +/// just for the `Conf::hash` part. +#[rpc(server, client, namespace = "foo", client_bounds(Conf::Hash: jsonrpsee::core::DeserializeOwned), server_bounds(Conf::Hash: jsonrpsee::core::Serialize))] +pub trait Rpc { + #[method(name = "bar")] + fn method(&self) -> Result; +} + +pub struct RpcServerImpl; + +#[async_trait] +impl RpcServer for RpcServerImpl { + fn method(&self) -> Result<::Hash, Error> { + Ok([0u8; 32]) + } +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + tracing_subscriber::FmtSubscriber::builder() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .try_init() + .expect("setting default subscriber failed"); + + let (server_addr, _handle) = run_server().await?; + let url = format!("ws://{}", server_addr); + + let client = WsClientBuilder::default().build(&url).await?; + assert_eq!(RpcClient::::method(&client).await.unwrap(), [0u8; 32]); + + Ok(()) +} + +async fn run_server() -> anyhow::Result<(SocketAddr, WsServerHandle)> { + let server = WsServerBuilder::default().build("127.0.0.1:0").await?; + + let addr = server.local_addr()?; + let handle = server.start(RpcServerImpl.into_rpc())?; + Ok((addr, handle)) +} From 721a18e8e7a09bef04a109cf489e0cae03e44ab5 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 24 Jun 2022 15:22:30 +0300 Subject: [PATCH 05/12] Check against client_bounds wihtout client implementation Signed-off-by: Alexandru Vasile --- proc-macros/src/rpc_macro.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/proc-macros/src/rpc_macro.rs b/proc-macros/src/rpc_macro.rs index 0bd9eb0e25..bafd93be86 100644 --- a/proc-macros/src/rpc_macro.rs +++ b/proc-macros/src/rpc_macro.rs @@ -241,6 +241,20 @@ impl RpcDescription { return Err(syn::Error::new_spanned(&item.ident, "Either 'server' or 'client' attribute must be applied")); } + if client_bounds.is_some() && !needs_client { + return Err(syn::Error::new_spanned( + &item.ident, + "Attribute 'client' must be specified with 'client_bounds'", + )); + } + + if server_bounds.is_some() && !needs_server { + return Err(syn::Error::new_spanned( + &item.ident, + "Attribute 'server' must be specified with 'server_bounds'", + )); + } + let jsonrpsee_client_path = crate::helpers::find_jsonrpsee_client_crate().ok(); let jsonrpsee_server_path = crate::helpers::find_jsonrpsee_server_crate().ok(); From a6e84041d5ec5a0f8bcb13fe626bc860ee1e067f Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 24 Jun 2022 15:29:39 +0300 Subject: [PATCH 06/12] tests: Add ui test for empty bounds Signed-off-by: Alexandru Vasile --- .../ui/incorrect/rpc/rpc_empty_bounds.rs | 16 ++++++++++++ .../ui/incorrect/rpc/rpc_empty_bounds.stderr | 26 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.rs create mode 100644 proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.stderr diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.rs b/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.rs new file mode 100644 index 0000000000..f4fcdce23c --- /dev/null +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.rs @@ -0,0 +1,16 @@ +use jsonrpsee::proc_macros::rpc; +use jsonrpsee::core::Error; + +pub trait Config { + type Hash: Send + Sync + 'static; +} + +/// Client bound must be `Conf::Hash: jsonrpsee::core::DeserializeOwned` +/// Server bound must be `Conf::Hash: jsonrpsee::core::Serialize` +#[rpc(server, client, namespace = "foo", client_bounds(), server_bounds())] +pub trait EmptyBounds { + #[method(name = "bar")] + fn method(&self) -> Result; +} + +fn main() {} diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.stderr b/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.stderr new file mode 100644 index 0000000000..ec2f0f0892 --- /dev/null +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.stderr @@ -0,0 +1,26 @@ +error[E0277]: the trait bound `::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 `::Hash` + | +note: required by a bound in `RpcModule::::register_method` + --> $WORKSPACE/core/src/server/rpc_module.rs + | + | R: Serialize, + | ^^^^^^^^^ required by this bound in `RpcModule::::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> ::Hash: Deserialize<'de>` 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 `for<'de> Deserialize<'de>` is not implemented for `::Hash` + | + = note: required because of the requirements on the impl of `DeserializeOwned` for `::Hash` +note: required by a bound in `request` + --> $WORKSPACE/core/src/client/mod.rs + | + | R: DeserializeOwned; + | ^^^^^^^^^^^^^^^^ required by this bound in `request` + = note: this error originates in the attribute macro `rpc` (in Nightly builds, run with -Z macro-backtrace for more info) From fdb15154bfb398704c40a49beac6e13655195405 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 24 Jun 2022 15:33:47 +0300 Subject: [PATCH 07/12] tests: Add ui test to check bounds without implementation Signed-off-by: Alexandru Vasile --- .../incorrect/rpc/rpc_bounds_without_impl.rs | 19 +++++++++++++++++++ .../rpc/rpc_bounds_without_impl.stderr | 11 +++++++++++ 2 files changed, 30 insertions(+) create mode 100644 proc-macros/tests/ui/incorrect/rpc/rpc_bounds_without_impl.rs create mode 100644 proc-macros/tests/ui/incorrect/rpc/rpc_bounds_without_impl.stderr diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_bounds_without_impl.rs b/proc-macros/tests/ui/incorrect/rpc/rpc_bounds_without_impl.rs new file mode 100644 index 0000000000..df727436cf --- /dev/null +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_bounds_without_impl.rs @@ -0,0 +1,19 @@ +use jsonrpsee::proc_macros::rpc; + +pub trait Config { + type Hash: Send + Sync + 'static; +} + +#[rpc(server, client_bounds(), server_bounds(Conf::Hash: jsonrpsee::core::Serialize))] +pub trait ClientBoundsForbidden { + #[method(name = "bar")] + fn method(&self) -> Result; +} + +#[rpc(client, server_bounds(), client_bounds(Conf::Hash: jsonrpsee::core::DeserializeOwned))] +pub trait ServerBoundsForbidden { + #[method(name = "bar")] + fn method(&self) -> Result; +} + +fn main() {} diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_bounds_without_impl.stderr b/proc-macros/tests/ui/incorrect/rpc/rpc_bounds_without_impl.stderr new file mode 100644 index 0000000000..30b3ed6aa2 --- /dev/null +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_bounds_without_impl.stderr @@ -0,0 +1,11 @@ +error: Attribute 'client' must be specified with 'client_bounds' + --> tests/ui/incorrect/rpc/rpc_bounds_without_impl.rs:8:11 + | +8 | pub trait ClientBoundsForbidden { + | ^^^^^^^^^^^^^^^^^^^^^ + +error: Attribute 'server' must be specified with 'server_bounds' + --> tests/ui/incorrect/rpc/rpc_bounds_without_impl.rs:14:11 + | +14 | pub trait ServerBoundsForbidden { + | ^^^^^^^^^^^^^^^^^^^^^ From 28ea2f0c9473e89372598ff473563aae4c3457f3 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 24 Jun 2022 15:43:47 +0300 Subject: [PATCH 08/12] Add bounds documentation Signed-off-by: Alexandru Vasile --- proc-macros/src/lib.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/proc-macros/src/lib.rs b/proc-macros/src/lib.rs index fed781d73a..87168af200 100644 --- a/proc-macros/src/lib.rs +++ b/proc-macros/src/lib.rs @@ -144,6 +144,10 @@ pub(crate) mod visitor; /// implementation's methods conveniently. /// - `namespace`: add a prefix to all the methods and subscriptions in this RPC. For example, with namespace `foo` and /// method `spam`, the resulting method name will be `foo_spam`. +/// - `server_bounds`: replace *all* auto-generated trait bounds with the user-defined ones for the server +/// implementation. +/// - `client_bounds`: replace *all* auto-generated trait bounds with the user-defined ones for the client +/// implementation. /// /// **Trait requirements:** /// @@ -287,7 +291,7 @@ pub(crate) mod visitor; /// // The stream API can be used to pipe items from the underlying stream /// // as subscription responses. /// fn sub_override_notif_method(&self, pending: PendingSubscription) { -/// let mut sink = pending.accept().unwrap(); +/// let mut sink = pending.accept().unwrap(); /// tokio::spawn(async move { /// let stream = futures_util::stream::iter(["one", "two", "three"]); /// sink.pipe_from_stream(stream).await; From 018332bc605dd8d8c151cbbc0c180542157d6e5e Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 24 Jun 2022 15:54:13 +0300 Subject: [PATCH 09/12] rpc_macro: Remove `WherePredicate` from parsing Signed-off-by: Alexandru Vasile --- proc-macros/src/rpc_macro.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proc-macros/src/rpc_macro.rs b/proc-macros/src/rpc_macro.rs index bafd93be86..bac70c3cc6 100644 --- a/proc-macros/src/rpc_macro.rs +++ b/proc-macros/src/rpc_macro.rs @@ -234,8 +234,8 @@ impl RpcDescription { let needs_server = optional(server, Argument::flag)?.is_some(); let needs_client = optional(client, Argument::flag)?.is_some(); let namespace = optional(namespace, Argument::string)?; - let client_bounds = optional(client_bounds, Argument::group::)?; - let server_bounds = optional(server_bounds, Argument::group::)?; + let client_bounds = optional(client_bounds, Argument::group)?; + let server_bounds = optional(server_bounds, Argument::group)?; if !needs_server && !needs_client { return Err(syn::Error::new_spanned(&item.ident, "Either 'server' or 'client' attribute must be applied")); From 019c189ae90e68f33a571ec49cc5ff07bcb19ef3 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 28 Jun 2022 20:26:11 +0300 Subject: [PATCH 10/12] ui: Add test that compiles Signed-off-by: Alexandru Vasile --- proc-macros/tests/ui/correct/rpc_bounds.rs | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 proc-macros/tests/ui/correct/rpc_bounds.rs diff --git a/proc-macros/tests/ui/correct/rpc_bounds.rs b/proc-macros/tests/ui/correct/rpc_bounds.rs new file mode 100644 index 0000000000..436e67ebdb --- /dev/null +++ b/proc-macros/tests/ui/correct/rpc_bounds.rs @@ -0,0 +1,27 @@ +use jsonrpsee::proc_macros::rpc; +use jsonrpsee::core::RpcResult; + +pub trait Config { + type Hash: Send + Sync + 'static; + type NotUsed; +} + +#[rpc(client, namespace = "foo", client_bounds(Conf::Hash: jsonrpsee::core::DeserializeOwned))] +pub trait MyRpcClient { + #[method(name = "bar")] + fn method(&self) -> RpcResult; +} + +#[rpc(server, namespace = "foo", server_bounds(Conf::Hash: jsonrpsee::core::Serialize))] +pub trait MyRpcServer { + #[method(name = "bar")] + fn method(&self) -> RpcResult; +} + +#[rpc(server, client, namespace = "foo", client_bounds(Conf::Hash: jsonrpsee::core::DeserializeOwned), server_bounds(Conf::Hash: jsonrpsee::core::Serialize))] +pub trait MyRpcServerClient { + #[method(name = "bar")] + fn method(&self) -> RpcResult; +} + +fn main() {} From 5801e230e48d13585a0b4b540087c45332505b8a Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 28 Jun 2022 20:48:29 +0300 Subject: [PATCH 11/12] Rename rendered `T` to avoid collision with user provided generic Signed-off-by: Alexandru Vasile --- examples/examples/proc_macro_bounds.rs | 6 +++--- proc-macros/src/render_client.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/examples/proc_macro_bounds.rs b/examples/examples/proc_macro_bounds.rs index 1f77991d47..6e4ab6daaa 100644 --- a/examples/examples/proc_macro_bounds.rs +++ b/examples/examples/proc_macro_bounds.rs @@ -50,10 +50,10 @@ impl Config for ExampleHash { /// /// Explicitly, specify client and server bounds to handle the `Serialize` and `DeserializeOwned` cases /// just for the `Conf::hash` part. -#[rpc(server, client, namespace = "foo", client_bounds(Conf::Hash: jsonrpsee::core::DeserializeOwned), server_bounds(Conf::Hash: jsonrpsee::core::Serialize))] -pub trait Rpc { +#[rpc(server, client, namespace = "foo", client_bounds(T::Hash: jsonrpsee::core::DeserializeOwned), server_bounds(T::Hash: jsonrpsee::core::Serialize))] +pub trait Rpc { #[method(name = "bar")] - fn method(&self) -> Result; + fn method(&self) -> Result; } pub struct RpcServerImpl; diff --git a/proc-macros/src/render_client.rs b/proc-macros/src/render_client.rs index db70c415dc..32e501556a 100644 --- a/proc-macros/src/render_client.rs +++ b/proc-macros/src/render_client.rs @@ -63,7 +63,7 @@ impl RpcDescription { #(#sub_impls)* } - impl #trait_name #type_generics for T where T: #super_trait #(,#where_clause)* {} + impl #trait_name #type_generics for TypeJsonRpseeInteral where TypeJsonRpseeInteral: #super_trait #(,#where_clause)* {} }; Ok(trait_impl) From 0bcf73818d9bcd68c3f7434de6b7080b0a2e9d60 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 29 Jun 2022 13:40:12 +0300 Subject: [PATCH 12/12] tests: Modify UI correct rpc_bounds test to call server's methods Signed-off-by: Alexandru Vasile --- proc-macros/tests/ui/correct/rpc_bounds.rs | 73 ++++++++++++++++++++-- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/proc-macros/tests/ui/correct/rpc_bounds.rs b/proc-macros/tests/ui/correct/rpc_bounds.rs index 436e67ebdb..e299af0ffe 100644 --- a/proc-macros/tests/ui/correct/rpc_bounds.rs +++ b/proc-macros/tests/ui/correct/rpc_bounds.rs @@ -1,27 +1,90 @@ +//! Example of using proc macro to generate working client and server with bounds applied. + +use std::net::SocketAddr; + +use jsonrpsee::core::{async_trait, RpcResult}; use jsonrpsee::proc_macros::rpc; -use jsonrpsee::core::RpcResult; +use jsonrpsee::ws_client::*; +use jsonrpsee::ws_server::WsServerBuilder; pub trait Config { type Hash: Send + Sync + 'static; type NotUsed; } +type ExampleHash = [u8; 32]; +impl Config for ExampleHash { + type Hash = Self; + type NotUsed = (); +} + +/// Client only RPC. #[rpc(client, namespace = "foo", client_bounds(Conf::Hash: jsonrpsee::core::DeserializeOwned))] -pub trait MyRpcClient { +pub trait MyRpcC { #[method(name = "bar")] fn method(&self) -> RpcResult; } +/// Server only RPC. #[rpc(server, namespace = "foo", server_bounds(Conf::Hash: jsonrpsee::core::Serialize))] -pub trait MyRpcServer { +pub trait MyRpcS { #[method(name = "bar")] fn method(&self) -> RpcResult; } +/// Client and server RPC. #[rpc(server, client, namespace = "foo", client_bounds(Conf::Hash: jsonrpsee::core::DeserializeOwned), server_bounds(Conf::Hash: jsonrpsee::core::Serialize))] -pub trait MyRpcServerClient { +pub trait MyRpcSC { #[method(name = "bar")] fn method(&self) -> RpcResult; } -fn main() {} +/// Implementation for the `MyRpcS` trait (server only). +pub struct ServerOnlyImpl; +#[async_trait] +impl MyRpcSServer for ServerOnlyImpl { + fn method(&self) -> RpcResult<::Hash> { + Ok([0u8; 32]) + } +} + +/// Implementation for the `MyRpcSC` trait (client server rpc). +pub struct ServerClientServerImpl; +#[async_trait] +impl MyRpcSCServer for ServerClientServerImpl { + fn method(&self) -> RpcResult<::Hash> { + Ok([0u8; 32]) + } +} + +pub async fn websocket_servers() -> (SocketAddr, SocketAddr) { + // Start server from `MyRpcS` trait. + let server = WsServerBuilder::default().build("127.0.0.1:0").await.unwrap(); + let addr_server_only = server.local_addr().unwrap(); + server.start(ServerOnlyImpl.into_rpc()).unwrap(); + + // Start server from `MyRpcSC` trait. + let server = WsServerBuilder::default().build("127.0.0.1:0").await.unwrap(); + let addr_server_client = server.local_addr().unwrap(); + server.start(ServerClientServerImpl.into_rpc()).unwrap(); + + (addr_server_only, addr_server_client) +} + +#[tokio::main] +async fn main() { + let (server_addr, server_addr_w_client) = websocket_servers().await; + let (server_url, server_w_client_url) = (format!("ws://{}", server_addr), format!("ws://{}", server_addr_w_client)); + let client = WsClientBuilder::default().build(&server_url).await.unwrap(); + let client_second = WsClientBuilder::default().build(&server_w_client_url).await.unwrap(); + + // Use `MyRpcC` client to communicate to the `MyRpcS` server. + assert_eq!(MyRpcCClient::::method(&client).await.unwrap(), [0u8; 32]); + // Use `MyRpcC` client to communicate to the `MyRpcSC` server. + assert_eq!(MyRpcCClient::::method(&client_second).await.unwrap(), [0u8; 32]); + + // Use `MyRpcSC` client to communicate to the `MyRpcS` server. + assert_eq!(MyRpcCClient::::method(&client).await.unwrap(), [0u8; 32]); + // Use `MyRpcSC` client to communicate to the `MyRpcSC` server. + assert_eq!(MyRpcSCClient::::method(&client_second).await.unwrap(), [0u8; 32]); +}