diff --git a/examples/proc_macro.rs b/examples/proc_macro.rs index 258611ac2b..b9f1706d01 100644 --- a/examples/proc_macro.rs +++ b/examples/proc_macro.rs @@ -26,17 +26,17 @@ use jsonrpsee::{ proc_macros::rpc, - types::async_trait, + types::{async_trait, error::Error}, ws_client::WsClientBuilder, ws_server::{RpcModule, SubscriptionSink, WsServerBuilder}, }; use std::net::SocketAddr; -#[rpc(client, server, namespace = "state")] +#[rpc(server, client, namespace = "state")] pub trait Rpc { /// Async method call example. #[method(name = "getPairs")] - async fn storage_pairs(&self, prefix: usize, hash: Option) -> Vec; + async fn storage_pairs(&self, prefix: usize, hash: Option) -> Result, Error>; /// Subscription that take `Option>` as input and produces output `Vec`. #[subscription(name = "subscribeStorage", unsub = "unsubscribeStorage", item = Vec)] @@ -47,8 +47,8 @@ pub struct RpcServerImpl; #[async_trait] impl RpcServer for RpcServerImpl { - async fn storage_pairs(&self, _prefix: usize, _hash: Option) -> Vec { - vec![1, 2, 3, 4] + async fn storage_pairs(&self, _prefix: usize, _hash: Option) -> Result, Error> { + Ok(vec![1, 2, 3, 4]) } fn subscribe_storage(&self, mut sink: SubscriptionSink, keys: Option>) { diff --git a/http-client/src/client.rs b/http-client/src/client.rs index 42bcf671b2..9e113ed28f 100644 --- a/http-client/src/client.rs +++ b/http-client/src/client.rs @@ -64,7 +64,7 @@ impl HttpClientBuilder { /// Build the HTTP client with target to connect to. pub fn build(self, target: impl AsRef) -> Result { let transport = - HttpTransportClient::new(target, self.max_request_body_size).map_err(|e| Error::Transport(Box::new(e)))?; + HttpTransportClient::new(target, self.max_request_body_size).map_err(|e| Error::Transport(e.into()))?; Ok(HttpClient { transport, request_id: AtomicU64::new(0), request_timeout: self.request_timeout }) } } @@ -94,7 +94,7 @@ impl Client for HttpClient { match tokio::time::timeout(self.request_timeout, fut).await { Ok(Ok(ok)) => Ok(ok), Err(_) => Err(Error::RequestTimeout), - Ok(Err(e)) => Err(Error::Transport(Box::new(e))), + Ok(Err(e)) => Err(Error::Transport(e.into())), } } @@ -111,7 +111,7 @@ impl Client for HttpClient { let body = match tokio::time::timeout(self.request_timeout, fut).await { Ok(Ok(body)) => body, Err(_e) => return Err(Error::RequestTimeout), - Ok(Err(e)) => return Err(Error::Transport(Box::new(e))), + Ok(Err(e)) => return Err(Error::Transport(e.into())), }; let response: JsonRpcResponse<_> = match serde_json::from_slice(&body) { @@ -152,7 +152,7 @@ impl Client for HttpClient { let body = match tokio::time::timeout(self.request_timeout, fut).await { Ok(Ok(body)) => body, Err(_e) => return Err(Error::RequestTimeout), - Ok(Err(e)) => return Err(Error::Transport(Box::new(e))), + Ok(Err(e)) => return Err(Error::Transport(e.into())), }; let rps: Vec> = match serde_json::from_slice(&body) { diff --git a/proc-macros/src/lib.rs b/proc-macros/src/lib.rs index 6bf57ef8c9..a710f285d6 100644 --- a/proc-macros/src/lib.rs +++ b/proc-macros/src/lib.rs @@ -198,16 +198,16 @@ mod new; /// /// // RPC is moved into a separate module to clearly show names of generated entities. /// mod rpc_impl { -/// use jsonrpsee::{proc_macros::rpc, types::async_trait, ws_server::SubscriptionSink}; +/// use jsonrpsee::{proc_macros::rpc, types::{async_trait, JsonRpcResult}, ws_server::SubscriptionSink}; /// /// // Generate both server and client implementations, prepend all the methods with `foo_` prefix. /// #[rpc(client, server, namespace = "foo")] /// pub trait Rpc { /// #[method(name = "foo")] -/// async fn async_method(&self, param_a: u8, param_b: String) -> u16; +/// async fn async_method(&self, param_a: u8, param_b: String) -> JsonRpcResult; /// /// #[method(name = "bar")] -/// fn sync_method(&self) -> u16; +/// fn sync_method(&self) -> JsonRpcResult; /// /// #[subscription(name = "sub", unsub = "unsub", item = String)] /// fn sub(&self); @@ -220,12 +220,12 @@ mod new; /// // Note that the trait name we use is `RpcServer`, not `Rpc`! /// #[async_trait] /// impl RpcServer for RpcServerImpl { -/// async fn async_method(&self, _param_a: u8, _param_b: String) -> u16 { -/// 42u16 +/// async fn async_method(&self, _param_a: u8, _param_b: String) -> JsonRpcResult { +/// Ok(42u16) /// } /// -/// fn sync_method(&self) -> u16 { -/// 10u16 +/// fn sync_method(&self) -> JsonRpcResult { +/// Ok(10u16) /// } /// /// // We could've spawned a `tokio` future that yields values while our program works, diff --git a/proc-macros/src/new/render_client.rs b/proc-macros/src/new/render_client.rs index 0d2cff38ec..41ebd0454b 100644 --- a/proc-macros/src/new/render_client.rs +++ b/proc-macros/src/new/render_client.rs @@ -52,7 +52,7 @@ impl RpcDescription { // `returns` represent the return type of the *rust method* (`Result< <..>, jsonrpsee::Error`). let (called_method, returns) = if let Some(returns) = &method.returns { let called_method = quote::format_ident!("request"); - let returns = quote! { Result<#returns, #jrps_error> }; + let returns = quote! { #returns }; (called_method, returns) } else { diff --git a/proc-macros/src/new/render_server.rs b/proc-macros/src/new/render_server.rs index f8a5a4d83f..80e36c6d59 100644 --- a/proc-macros/src/new/render_server.rs +++ b/proc-macros/src/new/render_server.rs @@ -25,8 +25,6 @@ impl RpcDescription { } }; - // panic!("{}", trait_impl); - Ok(trait_impl) } @@ -83,7 +81,7 @@ impl RpcDescription { let rpc_method_name = self.rpc_identifier(&method.name); // `parsing` is the code associated with parsing structure from the // provided `RpcParams` object. - // `params_seq` is the comma-delimited sequence of parametsrs. + // `params_seq` is the comma-delimited sequence of parameters. let (parsing, params_seq) = self.render_params_decoding(&method.params); check_name(rpc_method_name.clone(), rust_method_name.span()); @@ -93,7 +91,7 @@ impl RpcDescription { rpc.register_async_method(#rpc_method_name, |params, context| { let fut = async move { #parsing - Ok(context.as_ref().#rust_method_name(#params_seq).await) + context.as_ref().#rust_method_name(#params_seq).await }; Box::pin(fut) }) @@ -102,7 +100,7 @@ impl RpcDescription { handle_register_result(quote! { rpc.register_method(#rpc_method_name, |params, context| { #parsing - Ok(context.#rust_method_name(#params_seq)) + context.#rust_method_name(#params_seq) }) }) } @@ -121,7 +119,7 @@ impl RpcDescription { let rpc_unsub_name = self.rpc_identifier(&sub.unsub_method); // `parsing` is the code associated with parsing structure from the // provided `RpcParams` object. - // `params_seq` is the comma-delimited sequence of parametsrs. + // `params_seq` is the comma-delimited sequence of parameters. let (parsing, params_seq) = self.render_params_decoding(&sub.params); check_name(rpc_sub_name.clone(), rust_method_name.span()); diff --git a/proc-macros/tests/rpc_example.rs b/proc-macros/tests/rpc_example.rs index f2dbdd1859..d9f2c1910f 100644 --- a/proc-macros/tests/rpc_example.rs +++ b/proc-macros/tests/rpc_example.rs @@ -1,15 +1,16 @@ //! Example of using proc macro to generate working client and server. +use jsonrpsee::types::Error; use jsonrpsee_proc_macros::rpc; use std::borrow::Cow; #[rpc(client, server, namespace = "foo")] pub trait Rpc { #[method(name = "foo")] - async fn async_method(&self, param_a: u8, param_b: Option>) -> u16; + async fn async_method(&self, param_a: u8, param_b: Option>) -> Result; #[method(name = "bar")] - fn sync_method(&self) -> u16; + fn sync_method(&self) -> Result; #[subscription(name = "sub", unsub = "unsub", item = String)] fn sub(&self); diff --git a/proc-macros/tests/ui/correct/basic.rs b/proc-macros/tests/ui/correct/basic.rs index f310ece891..2b505b009a 100644 --- a/proc-macros/tests/ui/correct/basic.rs +++ b/proc-macros/tests/ui/correct/basic.rs @@ -2,7 +2,7 @@ use jsonrpsee::{ proc_macros::rpc, - types::async_trait, + types::{async_trait, JsonRpcResult}, ws_client::*, ws_server::{SubscriptionSink, WsServerBuilder}, }; @@ -11,10 +11,10 @@ use std::{net::SocketAddr, sync::mpsc::channel}; #[rpc(client, server, namespace = "foo")] pub trait Rpc { #[method(name = "foo")] - async fn async_method(&self, param_a: u8, param_b: String) -> u16; + async fn async_method(&self, param_a: u8, param_b: String) -> JsonRpcResult; #[method(name = "bar")] - fn sync_method(&self) -> u16; + fn sync_method(&self) -> JsonRpcResult; #[subscription(name = "sub", unsub = "unsub", item = String)] fn sub(&self); @@ -27,12 +27,12 @@ pub struct RpcServerImpl; #[async_trait] impl RpcServer for RpcServerImpl { - async fn async_method(&self, _param_a: u8, _param_b: String) -> u16 { - 42u16 + async fn async_method(&self, _param_a: u8, _param_b: String) -> JsonRpcResult { + Ok(42u16) } - fn sync_method(&self) -> u16 { - 10u16 + fn sync_method(&self) -> JsonRpcResult { + Ok(10u16) } fn sub(&self, mut sink: SubscriptionSink) { diff --git a/proc-macros/tests/ui/correct/only_client.rs b/proc-macros/tests/ui/correct/only_client.rs index c04d87e96c..ee38367ebc 100644 --- a/proc-macros/tests/ui/correct/only_client.rs +++ b/proc-macros/tests/ui/correct/only_client.rs @@ -1,14 +1,14 @@ //! Example of using proc macro to generate working client and server. -use jsonrpsee::proc_macros::rpc; +use jsonrpsee::{proc_macros::rpc, types::JsonRpcResult}; #[rpc(client)] pub trait Rpc { #[method(name = "foo")] - async fn async_method(&self, param_a: u8, param_b: String) -> u16; + async fn async_method(&self, param_a: u8, param_b: String) -> JsonRpcResult; #[method(name = "bar")] - fn sync_method(&self) -> u16; + fn sync_method(&self) -> JsonRpcResult; #[subscription(name = "sub", unsub = "unsub", item = String)] fn sub(&self); diff --git a/proc-macros/tests/ui/correct/only_server.rs b/proc-macros/tests/ui/correct/only_server.rs index 19922bfbe9..db3e96afe6 100644 --- a/proc-macros/tests/ui/correct/only_server.rs +++ b/proc-macros/tests/ui/correct/only_server.rs @@ -1,6 +1,6 @@ use jsonrpsee::{ proc_macros::rpc, - types::async_trait, + types::{async_trait, JsonRpcResult}, ws_server::{SubscriptionSink, WsServerBuilder}, }; use std::{net::SocketAddr, sync::mpsc::channel}; @@ -8,10 +8,10 @@ use std::{net::SocketAddr, sync::mpsc::channel}; #[rpc(server)] pub trait Rpc { #[method(name = "foo")] - async fn async_method(&self, param_a: u8, param_b: String) -> u16; + async fn async_method(&self, param_a: u8, param_b: String) -> JsonRpcResult; #[method(name = "bar")] - fn sync_method(&self) -> u16; + fn sync_method(&self) -> JsonRpcResult; #[subscription(name = "sub", unsub = "unsub", item = String)] fn sub(&self); @@ -21,12 +21,12 @@ pub struct RpcServerImpl; #[async_trait] impl RpcServer for RpcServerImpl { - async fn async_method(&self, _param_a: u8, _param_b: String) -> u16 { - 42u16 + async fn async_method(&self, _param_a: u8, _param_b: String) -> JsonRpcResult { + Ok(42u16) } - fn sync_method(&self) -> u16 { - 10u16 + fn sync_method(&self) -> JsonRpcResult { + Ok(10u16) } fn sub(&self, mut sink: SubscriptionSink) { diff --git a/proc-macros/tests/ui/incorrect/method/method_no_name.rs b/proc-macros/tests/ui/incorrect/method/method_no_name.rs index a3375d2f0d..226f28106d 100644 --- a/proc-macros/tests/ui/incorrect/method/method_no_name.rs +++ b/proc-macros/tests/ui/incorrect/method/method_no_name.rs @@ -4,7 +4,7 @@ use jsonrpsee::proc_macros::rpc; #[rpc(client, server)] pub trait NoMethodName { #[method()] - async fn async_method(&self) -> u8; + async fn async_method(&self) -> jsonrpsee::types::JsonRpcResult; } fn main() {} diff --git a/proc-macros/tests/ui/incorrect/method/method_unexpected_field.rs b/proc-macros/tests/ui/incorrect/method/method_unexpected_field.rs index c9b7c2e4d5..39111bff00 100644 --- a/proc-macros/tests/ui/incorrect/method/method_unexpected_field.rs +++ b/proc-macros/tests/ui/incorrect/method/method_unexpected_field.rs @@ -4,7 +4,7 @@ use jsonrpsee::proc_macros::rpc; #[rpc(client, server)] pub trait UnexpectedField { #[method(name = "foo", magic = false)] - async fn async_method(&self) -> u8; + async fn async_method(&self) -> jsonrpsee::types::JsonRpcResult; } fn main() {} diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_assoc_items.rs b/proc-macros/tests/ui/incorrect/rpc/rpc_assoc_items.rs index e84e7819e5..f15f6a2a30 100644 --- a/proc-macros/tests/ui/incorrect/rpc/rpc_assoc_items.rs +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_assoc_items.rs @@ -6,7 +6,7 @@ pub trait AssociatedConst { const WOO: usize; #[method(name = "foo")] - async fn async_method(&self) -> u8; + async fn async_method(&self) -> jsonrpsee::types::JsonRpcResult; } #[rpc(client, server)] @@ -14,7 +14,7 @@ pub trait AssociatedType { type Woo; #[method(name = "foo")] - async fn async_method(&self) -> u8; + async fn async_method(&self) -> jsonrpsee::types::JsonRpcResult; } fn main() {} diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_name_conflict.rs b/proc-macros/tests/ui/incorrect/rpc/rpc_name_conflict.rs index ac8f457453..1664abb0af 100644 --- a/proc-macros/tests/ui/incorrect/rpc/rpc_name_conflict.rs +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_name_conflict.rs @@ -1,13 +1,13 @@ -use jsonrpsee::proc_macros::rpc; +use jsonrpsee::{proc_macros::rpc, types::JsonRpcResult}; // Associated items are forbidden. #[rpc(client, server)] pub trait MethodNameConflict { #[method(name = "foo")] - async fn foo(&self) -> u8; + async fn foo(&self) -> JsonRpcResult; #[method(name = "foo")] - async fn bar(&self) -> u8; + async fn bar(&self) -> JsonRpcResult; } fn main() {} diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_name_conflict.stderr b/proc-macros/tests/ui/incorrect/rpc/rpc_name_conflict.stderr index 213f6fbacc..0ec9c07dd2 100644 --- a/proc-macros/tests/ui/incorrect/rpc/rpc_name_conflict.stderr +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_name_conflict.stderr @@ -1,5 +1,5 @@ error: "foo" is already defined --> $DIR/rpc_name_conflict.rs:10:11 | -10 | async fn bar(&self) -> u8; +10 | async fn bar(&self) -> JsonRpcResult; | ^^^ diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_no_impls.rs b/proc-macros/tests/ui/incorrect/rpc/rpc_no_impls.rs index 4e8e98dbb0..5649322fe2 100644 --- a/proc-macros/tests/ui/incorrect/rpc/rpc_no_impls.rs +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_no_impls.rs @@ -4,7 +4,7 @@ use jsonrpsee::proc_macros::rpc; #[rpc()] pub trait NoImpls { #[method(name = "foo")] - async fn async_method(&self) -> u8; + async fn async_method(&self) -> jsonrpsee::types::JsonRpcResult; } fn main() {} diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_not_qualified.rs b/proc-macros/tests/ui/incorrect/rpc/rpc_not_qualified.rs index 46d48d5161..92ac92e6b0 100644 --- a/proc-macros/tests/ui/incorrect/rpc/rpc_not_qualified.rs +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_not_qualified.rs @@ -3,7 +3,7 @@ use jsonrpsee::proc_macros::rpc; // Method without type marker. #[rpc(client, server)] pub trait NotQualified { - async fn async_method(&self) -> u8; + async fn async_method(&self) -> jsonrpsee::types::JsonRpcResult; } fn main() {} diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_not_qualified.stderr b/proc-macros/tests/ui/incorrect/rpc/rpc_not_qualified.stderr index 5f41617512..8fd2826f85 100644 --- a/proc-macros/tests/ui/incorrect/rpc/rpc_not_qualified.stderr +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_not_qualified.stderr @@ -1,5 +1,5 @@ error: Methods must have either 'method' or 'subscription' attribute --> $DIR/rpc_not_qualified.rs:6:2 | -6 | async fn async_method(&self) -> u8; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +6 | async fn async_method(&self) -> jsonrpsee::types::JsonRpcResult; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/tests/proc_macros.rs b/tests/tests/proc_macros.rs index d6d500bb17..59dcb3c2a1 100644 --- a/tests/tests/proc_macros.rs +++ b/tests/tests/proc_macros.rs @@ -33,15 +33,19 @@ use jsonrpsee::{ws_client::*, ws_server::WsServerBuilder}; use serde_json::value::RawValue; mod rpc_impl { - use jsonrpsee::{proc_macros::rpc, types::async_trait, ws_server::SubscriptionSink}; + use jsonrpsee::{ + proc_macros::rpc, + types::{async_trait, JsonRpcResult}, + ws_server::SubscriptionSink, + }; #[rpc(client, server, namespace = "foo")] pub trait Rpc { #[method(name = "foo")] - async fn async_method(&self, param_a: u8, param_b: String) -> u16; + async fn async_method(&self, param_a: u8, param_b: String) -> JsonRpcResult; #[method(name = "bar")] - fn sync_method(&self) -> u16; + fn sync_method(&self) -> JsonRpcResult; #[subscription(name = "sub", unsub = "unsub", item = String)] fn sub(&self); @@ -50,13 +54,13 @@ mod rpc_impl { fn sub_with_params(&self, val: u32); #[method(name = "params")] - fn params(&self, a: u8, b: &str) -> String { - format!("Called with: {}, {}", a, b) + fn params(&self, a: u8, b: &str) -> JsonRpcResult { + Ok(format!("Called with: {}, {}", a, b)) } #[method(name = "optional_params")] - fn optional_params(&self, a: u32, b: Option, c: Option) -> String { - format!("Called with: {}, {:?}, {:?}", a, b, c) + fn optional_params(&self, a: u32, b: Option, c: Option) -> JsonRpcResult { + Ok(format!("Called with: {}, {:?}, {:?}", a, b, c)) } #[method(name = "lifetimes")] @@ -66,13 +70,13 @@ mod rpc_impl { b: &'_ str, c: std::borrow::Cow<'_, str>, d: Option>, - ) -> String { - format!("Called with: {}, {}, {}, {:?}", a, b, c, d) + ) -> JsonRpcResult { + Ok(format!("Called with: {}, {}, {}, {:?}", a, b, c, d)) } #[method(name = "zero_copy_cow")] - fn zero_copy_cow(&self, a: std::borrow::Cow<'_, str>, b: beef::Cow<'_, str>) -> String { - format!("Zero copy params: {}, {}", matches!(a, std::borrow::Cow::Borrowed(_)), b.is_borrowed()) + fn zero_copy_cow(&self, a: std::borrow::Cow<'_, str>, b: beef::Cow<'_, str>) -> JsonRpcResult { + Ok(format!("Zero copy params: {}, {}", matches!(a, std::borrow::Cow::Borrowed(_)), b.is_borrowed())) } } @@ -80,12 +84,12 @@ mod rpc_impl { #[async_trait] impl RpcServer for RpcServerImpl { - async fn async_method(&self, _param_a: u8, _param_b: String) -> u16 { - 42u16 + async fn async_method(&self, _param_a: u8, _param_b: String) -> JsonRpcResult { + Ok(42u16) } - fn sync_method(&self) -> u16 { - 10u16 + fn sync_method(&self) -> JsonRpcResult { + Ok(10u16) } fn sub(&self, mut sink: SubscriptionSink) { diff --git a/types/Cargo.toml b/types/Cargo.toml index 86b4aa666b..cd18e52861 100644 --- a/types/Cargo.toml +++ b/types/Cargo.toml @@ -11,6 +11,7 @@ documentation = "https://docs.rs/jsonrpsee-types" [dependencies] async-trait = "0.1" +anyhow = "1" beef = { version = "0.5.1", features = ["impl_serde"] } futures-channel = { version = "0.3.14", features = ["sink"] } futures-util = { version = "0.3.14", default-features = false, features = ["std", "sink", "channel"] } diff --git a/types/src/client.rs b/types/src/client.rs index 5793d83357..d28e74a855 100644 --- a/types/src/client.rs +++ b/types/src/client.rs @@ -136,7 +136,7 @@ where Some(n) => match serde_json::from_value::>(n) { Ok(NotifResponse::Ok(parsed)) => Ok(Some(parsed)), Ok(NotifResponse::Err(e)) => Err(Error::SubscriptionClosed(e)), - Err(e) => Err(e.into()), + Err(e) => Err(Error::ParseError(e)), }, None => Ok(None), } diff --git a/types/src/error.rs b/types/src/error.rs index d58823ef51..b79dfe329d 100644 --- a/types/src/error.rs +++ b/types/src/error.rs @@ -25,7 +25,7 @@ pub enum CallError { InvalidParams, /// The call failed (let jsonrpsee assign default error code and error message). #[error("RPC Call failed: {0}")] - Failed(Box), + Failed(#[from] anyhow::Error), /// Custom error with specific JSON-RPC error code, message and data. #[error("RPC Call failed: code: {code}, message: {message}, data: {data:?}")] Custom { @@ -38,6 +38,24 @@ pub enum CallError { }, } +impl CallError { + /// Create `CallError` from a generic error. + pub fn from_std_error(err: E) -> Self + where + E: std::error::Error + Send + Sync + 'static, + { + CallError::Failed(err.into()) + } +} + +// NOTE(niklasad1): this `From` impl is a bit opinionated to regard all generic errors as `CallError`. +// In practice this should be the most common use case for users of this library. +impl From for Error { + fn from(err: anyhow::Error) -> Self { + Error::Call(CallError::Failed(err)) + } +} + /// Error type. #[derive(Debug, thiserror::Error)] pub enum Error { @@ -46,7 +64,7 @@ pub enum Error { Call(#[from] CallError), /// Networking error or error on the low-level protocol layer. #[error("Networking or low-level protocol error: {0}")] - Transport(#[source] Box), + Transport(#[source] anyhow::Error), /// JSON-RPC request error. #[error("JSON-RPC request error: {0:?}")] Request(String), @@ -103,6 +121,18 @@ pub enum Error { Custom(String), } +impl Error { + /// Create `Error::CallError` from a generic error. + /// Useful if you don't care about specific JSON-RPC error code and + /// just wants to return your custom error type. + pub fn to_call_error(err: E) -> Self + where + E: std::error::Error + Send + Sync + 'static, + { + Error::Call(CallError::from_std_error(err)) + } +} + /// Error type with a special `subscription_closed` field to detect that /// a subscription has been closed to distinguish valid items produced /// by the server on the subscription stream from an error. @@ -133,24 +163,24 @@ pub enum GenericTransportError { impl From for Error { fn from(io_err: std::io::Error) -> Error { - Error::Transport(Box::new(io_err)) + Error::Transport(io_err.into()) } } impl From for Error { fn from(handshake_err: soketto::handshake::Error) -> Error { - Error::Transport(Box::new(handshake_err)) + Error::Transport(handshake_err.into()) } } impl From for Error { fn from(conn_err: soketto::connection::Error) -> Error { - Error::Transport(Box::new(conn_err)) + Error::Transport(conn_err.into()) } } impl From for Error { fn from(hyper_err: hyper::Error) -> Error { - Error::Transport(Box::new(hyper_err)) + Error::Transport(hyper_err.into()) } } diff --git a/types/src/lib.rs b/types/src/lib.rs index e54f3e7e47..c9d8a52105 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -38,3 +38,6 @@ pub mod __reexports { pub use serde; pub use serde_json; } + +/// JSON-RPC result. +pub type JsonRpcResult = Result; diff --git a/types/src/v2/error.rs b/types/src/v2/error.rs index ed48f9b2c5..cdee6fbdc4 100644 --- a/types/src/v2/error.rs +++ b/types/src/v2/error.rs @@ -66,6 +66,8 @@ pub const INVALID_REQUEST_CODE: i32 = -32600; pub const METHOD_NOT_FOUND_CODE: i32 = -32601; /// Custom server error when a call failed. pub const CALL_EXECUTION_FAILED_CODE: i32 = -32000; +/// Unknown error. +pub const UNKNOWN_ERROR_CODE: i32 = -32001; /// Parse error message pub const PARSE_ERROR_MSG: &str = "Parse error"; diff --git a/utils/src/server/rpc_module.rs b/utils/src/server/rpc_module.rs index 6777e6865a..d44d0a447f 100644 --- a/utils/src/server/rpc_module.rs +++ b/utils/src/server/rpc_module.rs @@ -3,7 +3,9 @@ use beef::Cow; use futures_channel::{mpsc, oneshot}; use futures_util::{future::BoxFuture, FutureExt, StreamExt}; use jsonrpsee_types::error::{CallError, Error, SubscriptionClosedError}; -use jsonrpsee_types::v2::error::{JsonRpcErrorCode, JsonRpcErrorObject, CALL_EXECUTION_FAILED_CODE}; +use jsonrpsee_types::v2::error::{ + JsonRpcErrorCode, JsonRpcErrorObject, CALL_EXECUTION_FAILED_CODE, UNKNOWN_ERROR_CODE, +}; use jsonrpsee_types::v2::params::{ Id, JsonRpcSubscriptionParams, RpcParams, SubscriptionId as JsonRpcSubscriptionId, TwoPointZero, }; @@ -219,7 +221,7 @@ impl RpcModule { where Context: Send + Sync + 'static, R: Serialize, - F: Fn(RpcParams, &Context) -> Result + Send + Sync + 'static, + F: Fn(RpcParams, &Context) -> Result + Send + Sync + 'static, { self.methods.verify_method_name(method_name)?; @@ -230,8 +232,10 @@ impl RpcModule { MethodCallback::Sync(Arc::new(move |id, params, tx, _| { match callback(params, &*ctx) { Ok(res) => send_response(id, tx, res), - Err(CallError::InvalidParams) => send_error(id, tx, JsonRpcErrorCode::InvalidParams.into()), - Err(CallError::Failed(e)) => { + Err(Error::Call(CallError::InvalidParams)) => { + send_error(id, tx, JsonRpcErrorCode::InvalidParams.into()) + } + Err(Error::Call(CallError::Failed(e))) => { let err = JsonRpcErrorObject { code: JsonRpcErrorCode::ServerError(CALL_EXECUTION_FAILED_CODE), message: &e.to_string(), @@ -239,10 +243,20 @@ impl RpcModule { }; send_error(id, tx, err) } - Err(CallError::Custom { code, message, data }) => { + Err(Error::Call(CallError::Custom { code, message, data })) => { let err = JsonRpcErrorObject { code: code.into(), message: &message, data: data.as_deref() }; send_error(id, tx, err) } + // This should normally not happen because the most common use case is to + // return `Error::Call` in `register_method`. + Err(e) => { + let err = JsonRpcErrorObject { + code: JsonRpcErrorCode::ServerError(UNKNOWN_ERROR_CODE), + message: &e.to_string(), + data: None, + }; + send_error(id, tx, err) + } }; })), ); @@ -254,11 +268,7 @@ impl RpcModule { pub fn register_async_method(&mut self, method_name: &'static str, callback: F) -> Result<(), Error> where R: Serialize + Send + Sync + 'static, - F: Fn(RpcParams<'static>, Arc) -> BoxFuture<'static, Result> - + Copy - + Send - + Sync - + 'static, + F: Fn(RpcParams<'static>, Arc) -> BoxFuture<'static, Result> + Copy + Send + Sync + 'static, { self.methods.verify_method_name(method_name)?; @@ -271,8 +281,10 @@ impl RpcModule { let future = async move { match callback(params, ctx).await { Ok(res) => send_response(id, &tx, res), - Err(CallError::InvalidParams) => send_error(id, &tx, JsonRpcErrorCode::InvalidParams.into()), - Err(CallError::Failed(e)) => { + Err(Error::Call(CallError::InvalidParams)) => { + send_error(id, &tx, JsonRpcErrorCode::InvalidParams.into()) + } + Err(Error::Call(CallError::Failed(e))) => { let err = JsonRpcErrorObject { code: JsonRpcErrorCode::ServerError(CALL_EXECUTION_FAILED_CODE), message: &e.to_string(), @@ -280,11 +292,21 @@ impl RpcModule { }; send_error(id, &tx, err) } - Err(CallError::Custom { code, message, data }) => { + Err(Error::Call(CallError::Custom { code, message, data })) => { let err = JsonRpcErrorObject { code: code.into(), message: &message, data: data.as_deref() }; send_error(id, &tx, err) } + // This should normally not happen because the most common use case is to + // return `Error::Call` in `register_async_method`. + Err(e) => { + let err = JsonRpcErrorObject { + code: JsonRpcErrorCode::ServerError(UNKNOWN_ERROR_CODE), + message: &e.to_string(), + data: None, + }; + send_error(id, &tx, err) + } }; }; future.boxed() diff --git a/ws-client/src/client.rs b/ws-client/src/client.rs index 6b3072868c..eef86bb413 100644 --- a/ws-client/src/client.rs +++ b/ws-client/src/client.rs @@ -268,13 +268,13 @@ impl<'a> WsClientBuilder<'a> { let builder = WsTransportClientBuilder { certificate_store, - target: Target::parse(url).map_err(|e| Error::Transport(Box::new(e)))?, + target: Target::parse(url).map_err(|e| Error::Transport(e.into()))?, timeout: self.connection_timeout, origin_header: self.origin_header, max_request_body_size: self.max_request_body_size, }; - let (sender, receiver) = builder.build().await.map_err(|e| Error::Transport(Box::new(e)))?; + let (sender, receiver) = builder.build().await.map_err(|e| Error::Transport(e.into()))?; tokio::spawn(async move { background_task(sender, receiver, from_front, err_tx, max_capacity_per_subscription).await; @@ -562,7 +562,7 @@ async fn background_task( .expect("ID unused checked above; qed"), Err(e) => { log::warn!("[backend]: client request failed: {:?}", e); - let _ = request.send_back.map(|s| s.send(Err(Error::Transport(Box::new(e))))); + let _ = request.send_back.map(|s| s.send(Err(Error::Transport(e.into())))); } } } @@ -579,7 +579,7 @@ async fn background_task( .expect("Request ID unused checked above; qed"), Err(e) => { log::warn!("[backend]: client subscription failed: {:?}", e); - let _ = sub.send_back.send(Err(Error::Transport(Box::new(e)))); + let _ = sub.send_back.send(Err(Error::Transport(e.into()))); } }, // User dropped a subscription. @@ -669,7 +669,7 @@ async fn background_task( } Either::Right((Some(Err(e)), _)) => { log::error!("Error: {:?} terminating client", e); - let _ = front_error.send(Error::Transport(Box::new(e))); + let _ = front_error.send(Error::Transport(e.into())); return; } Either::Right((None, _)) => { diff --git a/ws-server/Cargo.toml b/ws-server/Cargo.toml index 4b0f73aa0a..394860299a 100644 --- a/ws-server/Cargo.toml +++ b/ws-server/Cargo.toml @@ -25,6 +25,7 @@ tokio-stream = { version = "0.1.1", features = ["net"] } tokio-util = { version = "0.6", features = ["compat"] } [dev-dependencies] +anyhow = "1" env_logger = "0.9" jsonrpsee-test-utils = { path = "../test-utils" } jsonrpsee-ws-client = { path = "../ws-client" } diff --git a/ws-server/src/tests.rs b/ws-server/src/tests.rs index eb23c97cd0..d4bf3ff55f 100644 --- a/ws-server/src/tests.rs +++ b/ws-server/src/tests.rs @@ -28,6 +28,7 @@ use crate::types::error::{CallError, Error}; use crate::{server::StopHandle, RpcModule, WsServerBuilder}; +use anyhow::anyhow; use futures_util::FutureExt; use jsonrpsee_test_utils::helpers::*; use jsonrpsee_test_utils::types::{Id, TestContext, WebSocketTestClient}; @@ -95,8 +96,8 @@ async fn server_with_handles() -> (SocketAddr, JoinHandle<()>, StopHandle) { .boxed() }) .unwrap(); - module.register_method("invalid_params", |_params, _| Err::<(), _>(CallError::InvalidParams)).unwrap(); - module.register_method("call_fail", |_params, _| Err::<(), _>(CallError::Failed(Box::new(MyAppError)))).unwrap(); + module.register_method("invalid_params", |_params, _| Err::<(), _>(CallError::InvalidParams.into())).unwrap(); + module.register_method("call_fail", |_params, _| Err::<(), _>(Error::to_call_error(MyAppError))).unwrap(); module .register_method("sleep_for", |params, _| { let sleep: Vec = params.parse()?; @@ -149,7 +150,7 @@ async fn server_with_context() -> SocketAddr { async move { let _ = ctx.ok().map_err(|e| CallError::Failed(e.into()))?; // Async work that returns an error - futures_util::future::err::<(), CallError>(CallError::Failed(String::from("nah").into())).await + futures_util::future::err::<(), _>(anyhow!("nah").into()).await } .boxed() })