From f1e4ad7ce08695fbce23616d983e3324ec68000e Mon Sep 17 00:00:00 2001 From: Niklas Adolfsson Date: Mon, 24 Apr 2023 12:04:43 +0200 Subject: [PATCH 1/2] fix(proc macros): support parsing params !Result --- core/src/server/mod.rs | 19 ++++ examples/Cargo.toml | 1 + proc-macros/src/render_server.rs | 30 +++--- .../tests/ui/correct/custom_ret_types.rs | 93 ++++++++++++++++--- .../ui/incorrect/rpc/rpc_empty_bounds.stderr | 10 -- tests/tests/proc_macros.rs | 32 ++++++- 6 files changed, 148 insertions(+), 37 deletions(-) diff --git a/core/src/server/mod.rs b/core/src/server/mod.rs index 5aa7e4db75..4ff9d729c3 100644 --- a/core/src/server/mod.rs +++ b/core/src/server/mod.rs @@ -104,6 +104,25 @@ where } } +impl IntoResponse for ResponsePayload<'static, T> +where + T: serde::Serialize + Clone, +{ + type Output = T; + + fn into_response(self) -> ResponsePayload<'static, Self::Output> { + self + } +} + +impl IntoResponse for ErrorObjectOwned { + type Output = ErrorObjectOwned; + + fn into_response(self) -> ResponsePayload<'static, Self::Output> { + ResponsePayload::Error(self) + } +} + macro_rules! impl_into_response { ($($n:ty),*) => { $( diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 7afa4dcec1..eceb3798e7 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -15,6 +15,7 @@ tracing = "0.1.34" tracing-subscriber = { version = "0.3.3", features = ["env-filter"] } tokio = { version = "1.16", features = ["full"] } tokio-stream = { version = "0.1", features = ["sync"] } +serde = "1" serde_json = { version = "1" } tower-http = { version = "0.4.0", features = ["full"] } tower = { version = "0.4.13", features = ["full"] } diff --git a/proc-macros/src/render_server.rs b/proc-macros/src/render_server.rs index 15c67e7e9b..ba8b8934ed 100644 --- a/proc-macros/src/render_server.rs +++ b/proc-macros/src/render_server.rs @@ -129,13 +129,15 @@ impl RpcDescription { // called.. let (parsing, params_seq) = self.render_params_decoding(&method.params, None); + let into_response = self.jrps_server_item(quote! { IntoResponse }); + check_name(&rpc_method_name, rust_method_name.span()); if method.signature.sig.asyncness.is_some() { handle_register_result(quote! { rpc.register_async_method(#rpc_method_name, |params, context| async move { #parsing - context.as_ref().#rust_method_name(#params_seq).await + #into_response::into_response(context.as_ref().#rust_method_name(#params_seq).await) }) }) } else { @@ -145,7 +147,7 @@ impl RpcDescription { handle_register_result(quote! { rpc.#register_kind(#rpc_method_name, |params, context| { #parsing - context.#rust_method_name(#params_seq) + #into_response::into_response(context.#rust_method_name(#params_seq)) }) }) } @@ -286,7 +288,7 @@ impl RpcDescription { let params_fields = quote! { #(#params_fields_seq),* }; let tracing = self.jrps_server_item(quote! { tracing }); let sub_err = self.jrps_server_item(quote! { SubscriptionCloseResponse }); - let err_obj = self.jrps_server_item(quote! { types::ErrorObjectOwned }); + let response_payload = self.jrps_server_item(quote! { types::ResponsePayload }); // Code to decode sequence of parameters from a JSON array. let decode_array = { @@ -297,7 +299,7 @@ impl RpcDescription { Ok(v) => v, Err(e) => { #tracing::warn!(concat!("Error parsing optional \"", stringify!(#name), "\" as \"", stringify!(#ty), "\": {:?}"), e); - #pending.reject(#err_obj::from(e)).await; + #pending.reject(e).await; return #sub_err::None; } }; @@ -309,7 +311,7 @@ impl RpcDescription { Ok(v) => v, Err(e) => { #tracing::warn!(concat!("Error parsing optional \"", stringify!(#name), "\" as \"", stringify!(#ty), "\": {:?}"), e); - return Err(e.into()) + return #response_payload::Error(e); } }; } @@ -320,7 +322,7 @@ impl RpcDescription { Ok(v) => v, Err(e) => { #tracing::warn!(concat!("Error parsing optional \"", stringify!(#name), "\" as \"", stringify!(#ty), "\": {:?}"), e); - #pending.reject(#err_obj::from(e)).await; + #pending.reject(e).await; return #sub_err::None; } }; @@ -332,7 +334,7 @@ impl RpcDescription { Ok(v) => v, Err(e) => { #tracing::warn!(concat!("Error parsing \"", stringify!(#name), "\" as \"", stringify!(#ty), "\": {:?}"), e); - return Err(e.into()) + return #response_payload::Error(e); } }; } @@ -398,7 +400,7 @@ impl RpcDescription { Ok(p) => p, Err(e) => { #tracing::warn!("Failed to parse JSON-RPC params as object: {}", e); - #pending.reject(#err_obj::from(e)).await; + #pending.reject(e).await; return #sub_err::None; } }; @@ -413,10 +415,14 @@ impl RpcDescription { #(#fields)* } - let parsed: ParamsObject<#(#types,)*> = params.parse().map_err(|e| { - #tracing::warn!("Failed to parse JSON-RPC params as object: {}", e); - e - })?; + let parsed: ParamsObject<#(#types,)*> = match params.parse() { + Ok(p) => p, + Err(e) => { + #tracing::warn!("Failed to parse JSON-RPC params as object: {}", e); + return #response_payload::Error(e); + } + }; + (#(#destruct),*) } } diff --git a/proc-macros/tests/ui/correct/custom_ret_types.rs b/proc-macros/tests/ui/correct/custom_ret_types.rs index 8851e4d5db..c3995aa779 100644 --- a/proc-macros/tests/ui/correct/custom_ret_types.rs +++ b/proc-macros/tests/ui/correct/custom_ret_types.rs @@ -2,7 +2,7 @@ use std::net::SocketAddr; -use jsonrpsee::core::{async_trait, Serialize}; +use jsonrpsee::core::{async_trait, Error, Serialize}; use jsonrpsee::proc_macros::rpc; use jsonrpsee::server::{IntoResponse, ServerBuilder}; use jsonrpsee::types::ResponsePayload; @@ -37,23 +37,51 @@ impl IntoResponse for CustomError { #[rpc(server, namespace = "foo")] pub trait Rpc { - #[method(name = "method1")] - async fn method1(&self) -> CustomError; + #[method(name = "async_method1")] + async fn async_method1(&self) -> CustomError; - #[method(name = "method2")] - async fn method2(&self) -> CustomError; + #[method(name = "async_method2")] + async fn async_method2(&self, x: u32) -> CustomError; + + #[method(name = "sync_method1")] + fn method1(&self) -> CustomError; + + #[method(name = "sync_method2")] + fn method2(&self, x: u32) -> CustomError; + + #[method(name = "blocking_method1", blocking)] + fn blocking_method1(&self) -> CustomError; + + #[method(name = "blocking_method2", blocking)] + fn blocking_method2(&self, x: u32) -> CustomError; } pub struct RpcServerImpl; #[async_trait] impl RpcServer for RpcServerImpl { - async fn method1(&self) -> CustomError { + async fn async_method1(&self) -> CustomError { + CustomError::One + } + + async fn async_method2(&self, x: u32) -> CustomError { + CustomError::Two { custom_data: x } + } + + fn method1(&self) -> CustomError { + CustomError::One + } + + fn method2(&self, x: u32) -> CustomError { + CustomError::Two { custom_data: x } + } + + fn blocking_method1(&self) -> CustomError { CustomError::One } - async fn method2(&self) -> CustomError { - CustomError::Two { custom_data: 123 } + fn blocking_method2(&self, x: u32) -> CustomError { + CustomError::Two { custom_data: x } } } @@ -62,11 +90,23 @@ impl RpcServer for RpcServerImpl { // The client accepts only return types that are `Result`. #[rpc(client, namespace = "foo")] pub trait RpcClient { - #[method(name = "method1")] - async fn client_method1(&self) -> RpcResult; + #[method(name = "async_method1")] + async fn async_method1(&self) -> RpcResult; + + #[method(name = "async_method2")] + async fn async_method2(&self, x: u32) -> Result; + + #[method(name = "sync_method1")] + async fn sync_method1(&self) -> RpcResult; - #[method(name = "method2")] - async fn client_method2(&self) -> Result; + #[method(name = "sync_method2")] + async fn sync_method2(&self, x: u32) -> Result; + + #[method(name = "blocking_method1")] + async fn blocking_method1(&self) -> RpcResult; + + #[method(name = "blocking_method2")] + async fn blocking_method2(&self, x: u32) -> Result; } pub async fn server() -> SocketAddr { @@ -85,18 +125,43 @@ async fn main() { let server_url = format!("ws://{}", server_addr); let client = WsClientBuilder::default().build(&server_url).await.unwrap(); + let error = client.async_method1().await.unwrap_err(); + assert_method1(error); + + let error = client.async_method2(123).await.unwrap_err(); + assert_method2(error); + + let error = client.sync_method1().await.unwrap_err(); + assert_method1(error); + + let error = client.sync_method2(123).await.unwrap_err(); + assert_method2(error); + + let error = client.blocking_method1().await.unwrap_err(); + assert_method1(error); + + let error = client.blocking_method2(123).await.unwrap_err(); + assert_method2(error); +} + +fn assert_method1(error: Error) { let get_error_object = |err| match err { jsonrpsee::core::Error::Call(object) => object, _ => panic!("wrong error kind: {:?}", err), }; - let error = client.client_method1().await.unwrap_err(); let error_object = get_error_object(error); assert_eq!(error_object.code(), 101); assert_eq!(error_object.message(), "custom_error"); assert!(error_object.data().is_none()); +} + +fn assert_method2(error: Error) { + let get_error_object = |err| match err { + jsonrpsee::core::Error::Call(object) => object, + _ => panic!("wrong error kind: {:?}", err), + }; - let error = client.client_method2().await.unwrap_err(); let error_object = get_error_object(error); assert_eq!(error_object.code(), 102); assert_eq!(error_object.message(), "custom_error"); diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.stderr b/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.stderr index e1c9de4a3f..42049cceb5 100644 --- a/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.stderr +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.stderr @@ -5,11 +5,6 @@ error[E0277]: the trait bound `::Hash: Serialize` is not satisfi | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Serialize` is not implemented for `::Hash` | = note: required for `std::result::Result<::Hash, ErrorObject<'_>>` to implement `IntoResponse` -note: required by a bound in `RpcModule::::register_method` - --> $WORKSPACE/core/src/server/rpc_module.rs - | - | R: IntoResponse + 'static, - | ^^^^^^^^^^^^ 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 `::Hash: Clone` is not satisfied @@ -19,11 +14,6 @@ error[E0277]: the trait bound `::Hash: Clone` is not satisfied | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `::Hash` | = note: required for `std::result::Result<::Hash, ErrorObject<'_>>` to implement `IntoResponse` -note: required by a bound in `RpcModule::::register_method` - --> $WORKSPACE/core/src/server/rpc_module.rs - | - | R: IntoResponse + 'static, - | ^^^^^^^^^^^^ 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 diff --git a/tests/tests/proc_macros.rs b/tests/tests/proc_macros.rs index 5932672028..362bf43149 100644 --- a/tests/tests/proc_macros.rs +++ b/tests/tests/proc_macros.rs @@ -47,7 +47,7 @@ mod rpc_impl { }; use jsonrpsee::core::{async_trait, SubscriptionResult}; use jsonrpsee::proc_macros::rpc; - use jsonrpsee::types::ErrorObjectOwned; + use jsonrpsee::types::{ErrorObject, ErrorObjectOwned}; pub struct CustomSubscriptionRet; @@ -57,6 +57,14 @@ mod rpc_impl { } } + pub struct MyError; + + impl From for ErrorObjectOwned { + fn from(_: MyError) -> Self { + ErrorObject::owned(1, "my_error", None::<()>) + } + } + #[rpc(client, server, namespace = "foo")] pub trait Rpc { #[method(name = "foo")] @@ -114,6 +122,28 @@ mod rpc_impl { std::thread::sleep(std::time::Duration::from_millis(50)); Ok(42) } + + #[method(name = "blocking_call_custom_err", blocking)] + fn blocking_call_custom_err(&self) -> Result { + std::thread::sleep(std::time::Duration::from_millis(50)); + Ok(42) + } + + #[method(name = "blocking_call_custom_err_with_params", blocking)] + fn blocking_call_custom_err_with_params(&self, x: usize) -> Result { + std::thread::sleep(std::time::Duration::from_millis(50)); + Ok(x) + } + + #[method(name = "my_err")] + fn custom_error(&self) -> Result<(), MyError> { + Err(MyError) + } + + #[method(name = "my_err_with_param")] + fn custom_error_with_param(&self, _s: String) -> Result<(), MyError> { + Err(MyError) + } } pub struct RpcServerImpl; From 5e3289b74845746817034164afd62bd329a9bd54 Mon Sep 17 00:00:00 2001 From: Niklas Adolfsson Date: Mon, 24 Apr 2023 12:10:25 +0200 Subject: [PATCH 2/2] revert unintentional change --- examples/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/Cargo.toml b/examples/Cargo.toml index eceb3798e7..7afa4dcec1 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -15,7 +15,6 @@ tracing = "0.1.34" tracing-subscriber = { version = "0.3.3", features = ["env-filter"] } tokio = { version = "1.16", features = ["full"] } tokio-stream = { version = "0.1", features = ["sync"] } -serde = "1" serde_json = { version = "1" } tower-http = { version = "0.4.0", features = ["full"] } tower = { version = "0.4.13", features = ["full"] }