From d6a96229d69f76cab2f6d31be111165571d430e1 Mon Sep 17 00:00:00 2001 From: Niklas Adolfsson Date: Fri, 21 Apr 2023 16:40:34 +0200 Subject: [PATCH] remove `CallError` (#1087) * refactor: remove `CallError` * use error msg in InvalidParams * fix tests * remove unused dependency anyhow * Update core/src/error.rs * Update core/src/lib.rs * Update core/src/lib.rs * fix nit * fix error object display impl * remove invalid params * Update proc-macros/tests/ui/correct/only_client.rs Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> * fix UI tests on new rustc * invalid params: strictly jsonrpc spec * grumbles: unify type hints --------- Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> --- benches/helpers.rs | 2 +- client/http-client/src/client.rs | 4 +- client/http-client/src/tests.rs | 5 +- client/ws-client/src/tests.rs | 5 +- core/src/client/async_client/helpers.rs | 3 +- core/src/error.rs | 43 +--------- core/src/lib.rs | 2 +- core/src/server/helpers.rs | 8 +- core/src/server/rpc_module.rs | 9 +- examples/examples/http_proxy_middleware.rs | 3 +- examples/examples/proc_macro.rs | 11 ++- examples/examples/proc_macro_bounds.rs | 7 +- examples/examples/ws_pubsub_with_params.rs | 3 +- proc-macros/src/render_client.rs | 7 +- .../tests/ui/correct/custom_ret_types.rs | 4 +- proc-macros/tests/ui/correct/only_client.rs | 6 +- .../ui/incorrect/rpc/rpc_empty_bounds.rs | 5 +- .../ui/incorrect/rpc/rpc_empty_bounds.stderr | 66 +++++++-------- server/src/tests/helpers.rs | 84 ++++++++++--------- server/src/tests/http.rs | 30 +++---- server/src/tests/ws.rs | 9 +- test-utils/Cargo.toml | 1 - test-utils/src/mocks.rs | 13 +-- tests/tests/helpers.rs | 22 +++-- tests/tests/integration_tests.rs | 10 +-- tests/tests/metrics.rs | 4 +- tests/tests/proc_macros.rs | 46 ++++++---- tests/tests/rpc_module.rs | 55 ++++++------ types/src/error.rs | 37 +------- types/src/params.rs | 26 +++--- 30 files changed, 237 insertions(+), 293 deletions(-) diff --git a/benches/helpers.rs b/benches/helpers.rs index d360ee6b6c..ec633ad536 100644 --- a/benches/helpers.rs +++ b/benches/helpers.rs @@ -186,7 +186,7 @@ fn gen_rpc_module() -> jsonrpsee::RpcModule<()> { module .register_async_method(ASYNC_SLOW_CALL, |_, _| async move { tokio::time::sleep(SLOW_CALL).await; - Result::<_, jsonrpsee::core::Error>::Ok("slow call async") + "slow call async" }) .unwrap(); diff --git a/client/http-client/src/client.rs b/client/http-client/src/client.rs index b20cfd62e1..5cd91626fa 100644 --- a/client/http-client/src/client.rs +++ b/client/http-client/src/client.rs @@ -43,7 +43,6 @@ use jsonrpsee_core::client::{ use jsonrpsee_core::params::BatchRequestBuilder; use jsonrpsee_core::traits::ToRpcParams; use jsonrpsee_core::{Error, JsonRawValue, TEN_MB_SIZE_BYTES}; -use jsonrpsee_types::error::CallError; use jsonrpsee_types::{ErrorObject, ResponseSuccess, TwoPointZero}; use serde::de::DeserializeOwned; use tower::layer::util::Identity; @@ -300,8 +299,7 @@ where // NOTE: it's decoded first to `JsonRawValue` and then to `R` below to get // a better error message if `R` couldn't be decoded. - let response = ResponseSuccess::try_from(serde_json::from_slice::>(&body)?) - .map_err(|e| Error::Call(CallError::Custom(e)))?; + let response = ResponseSuccess::try_from(serde_json::from_slice::>(&body)?)?; let result = serde_json::from_str(response.result.get()).map_err(Error::ParseError)?; diff --git a/client/http-client/src/tests.rs b/client/http-client/src/tests.rs index 5dc78e8738..5474e658d1 100644 --- a/client/http-client/src/tests.rs +++ b/client/http-client/src/tests.rs @@ -34,7 +34,7 @@ use jsonrpsee_core::{rpc_params, DeserializeOwned}; use jsonrpsee_test_utils::helpers::*; use jsonrpsee_test_utils::mocks::Id; use jsonrpsee_test_utils::TimeoutFutureExt; -use jsonrpsee_types::error::{CallError, ErrorObjectOwned}; +use jsonrpsee_types::error::ErrorObjectOwned; fn init_logger() { let _ = tracing_subscriber::FmtSubscriber::builder() @@ -276,10 +276,9 @@ async fn run_request_with_response(response: String) -> Result { } fn assert_jsonrpc_error_response(err: Error, exp: ErrorObjectOwned) { - let exp = CallError::Custom(exp); match &err { Error::Call(err) => { - assert_eq!(err.to_string(), exp.to_string()); + assert_eq!(err, &exp); } e => panic!("Expected error: \"{err}\", got: {e:?}"), }; diff --git a/client/ws-client/src/tests.rs b/client/ws-client/src/tests.rs index 3494a0ae52..b4b94d080f 100644 --- a/client/ws-client/src/tests.rs +++ b/client/ws-client/src/tests.rs @@ -35,7 +35,7 @@ use jsonrpsee_core::{rpc_params, DeserializeOwned, Error}; use jsonrpsee_test_utils::helpers::*; use jsonrpsee_test_utils::mocks::{Id, WebSocketTestServer}; use jsonrpsee_test_utils::TimeoutFutureExt; -use jsonrpsee_types::error::{CallError, ErrorObjectOwned}; +use jsonrpsee_types::error::ErrorObjectOwned; use serde_json::Value as JsonValue; fn init_logger() { @@ -393,10 +393,9 @@ async fn run_request_with_response(response: String) -> Result { } fn assert_error_response(err: Error, exp: ErrorObjectOwned) { - let exp = CallError::Custom(exp); match &err { Error::Call(e) => { - assert_eq!(e.to_string(), exp.to_string()); + assert_eq!(e, &exp); } e => panic!("Expected error: \"{err}\", got: {e:?}"), }; diff --git a/core/src/client/async_client/helpers.rs b/core/src/client/async_client/helpers.rs index 2fc2aa388f..d5b965fb5a 100644 --- a/core/src/client/async_client/helpers.rs +++ b/core/src/client/async_client/helpers.rs @@ -34,7 +34,6 @@ use futures_timer::Delay; use futures_util::future::{self, Either}; use tokio::sync::{mpsc, oneshot}; -use jsonrpsee_types::error::CallError; use jsonrpsee_types::response::SubscriptionError; use jsonrpsee_types::{ ErrorObject, Id, Notification, RequestSer, Response, ResponseSuccess, SubscriptionId, SubscriptionResponse, @@ -176,7 +175,7 @@ pub(crate) fn process_single_response( max_capacity_per_subscription: usize, ) -> Result, Error> { let response_id = response.id.clone().into_owned(); - let result = ResponseSuccess::try_from(response).map(|s| s.result).map_err(|e| Error::Call(CallError::Custom(e))); + let result = ResponseSuccess::try_from(response).map(|s| s.result).map_err(Error::Call); match manager.request_status(&response_id) { RequestStatus::PendingMethodCall => { diff --git a/core/src/error.rs b/core/src/error.rs index 61688e0b99..4fba255efe 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -26,9 +26,7 @@ use std::fmt; -use jsonrpsee_types::error::{ - CallError, ErrorObject, ErrorObjectOwned, CALL_EXECUTION_FAILED_CODE, INVALID_PARAMS_CODE, UNKNOWN_ERROR_CODE, -}; +use jsonrpsee_types::error::ErrorObjectOwned; /// Convenience type for displaying errors. #[derive(Clone, Debug, PartialEq, Eq)] @@ -45,20 +43,12 @@ impl fmt::Display for Mismatch { } } -// 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 { - /// Error that occurs when a call failed. + /// JSON-RPC error which can occur when a JSON-RPC call fails. #[error("{0}")] - Call(#[from] CallError), + Call(#[from] ErrorObjectOwned), /// Networking error or error on the low-level protocol layer. #[error("Networking or low-level protocol error: {0}")] Transport(#[source] anyhow::Error), @@ -118,33 +108,6 @@ pub enum Error { EmptyBatchRequest, } -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)) - } -} - -impl From for ErrorObjectOwned { - fn from(err: Error) -> Self { - match err { - Error::Call(CallError::Custom(err)) => err, - Error::Call(CallError::InvalidParams(e)) => { - ErrorObject::owned(INVALID_PARAMS_CODE, e.to_string(), None::<()>) - } - Error::Call(CallError::Failed(e)) => { - ErrorObject::owned(CALL_EXECUTION_FAILED_CODE, e.to_string(), None::<()>) - } - _ => ErrorObject::owned(UNKNOWN_ERROR_CODE, err.to_string(), None::<()>), - } - } -} - /// Generic transport error. #[derive(Debug, thiserror::Error)] pub enum GenericTransportError { diff --git a/core/src/lib.rs b/core/src/lib.rs index 9031807ccd..bff45dfec0 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -62,7 +62,7 @@ pub use async_trait::async_trait; pub use error::{Error, GenericTransportError, StringError}; /// JSON-RPC result. -pub type RpcResult = std::result::Result; +pub type RpcResult = std::result::Result; /// Empty server `RpcParams` type to use while registering modules. pub type EmptyServerParams = Vec<()>; diff --git a/core/src/server/helpers.rs b/core/src/server/helpers.rs index 32f0fcf596..7209aa99ae 100644 --- a/core/src/server/helpers.rs +++ b/core/src/server/helpers.rs @@ -28,12 +28,11 @@ use std::io; use std::time::Duration; use crate::tracing::tx_log_from_str; -use crate::Error; use jsonrpsee_types::error::{ErrorCode, ErrorObject, OVERSIZED_RESPONSE_CODE, OVERSIZED_RESPONSE_MSG}; use jsonrpsee_types::{Id, InvalidRequest, Response, ResponsePayload}; use serde::Serialize; -use tokio::sync::mpsc::{self, OwnedPermit}; use serde_json::value::to_raw_value; +use tokio::sync::mpsc::{self, OwnedPermit}; use super::{DisconnectError, SendTimeoutError, SubscriptionMessage, TrySendError}; @@ -169,11 +168,6 @@ impl MethodSinkPermit { self.send_raw(json) } - /// Helper for sending the general purpose `Error` as a JSON-RPC errors to the client. - pub fn send_call_error(self, id: Id, err: Error) { - self.send_error(id, err.into()) - } - /// Send a raw JSON-RPC message to the client, `MethodSink` does not check the validity /// of the JSON being sent. pub fn send_raw(self, json: String) { diff --git a/core/src/server/rpc_module.rs b/core/src/server/rpc_module.rs index 834c265c03..9bfc04efff 100644 --- a/core/src/server/rpc_module.rs +++ b/core/src/server/rpc_module.rs @@ -41,7 +41,7 @@ use crate::server::subscription::{ }; use crate::traits::ToRpcParams; use futures_util::{future::BoxFuture, FutureExt}; -use jsonrpsee_types::error::{CallError, ErrorCode, ErrorObject}; +use jsonrpsee_types::error::{ErrorCode, ErrorObject}; use jsonrpsee_types::{ Id, Params, Request, Response, ResponsePayload, ResponseSuccess, SubscriptionId as RpcSubscriptionId, }; @@ -253,7 +253,7 @@ impl Methods { tracing::trace!("[Methods::call] Method: {:?}, params: {:?}", method, params); let (resp, _) = self.inner_call(req, 1, mock_subscription_permit()).await; let rp = serde_json::from_str::>(&resp.result)?; - ResponseSuccess::try_from(rp).map(|s| s.result).map_err(|e| Error::Call(CallError::Custom(e))) + ResponseSuccess::try_from(rp).map(|s| s.result).map_err(Error::Call) } /// Make a request (JSON-RPC method call or subscription) by using raw JSON. @@ -385,9 +385,8 @@ impl Methods { let (resp, rx) = self.inner_call(req, buf_size, mock_subscription_permit()).await; // TODO: hack around the lifetime on the `SubscriptionId` by deserialize first to serde_json::Value. - let as_success: ResponseSuccess = serde_json::from_str::>(&resp.result)? - .try_into() - .map_err(|e| Error::Call(CallError::Custom(e)))?; + let as_success: ResponseSuccess = + serde_json::from_str::>(&resp.result)?.try_into()?; let sub_id = as_success.result.try_into().map_err(|_| Error::InvalidSubscriptionId)?; diff --git a/examples/examples/http_proxy_middleware.rs b/examples/examples/http_proxy_middleware.rs index 93a95464db..fb9cf40d35 100644 --- a/examples/examples/http_proxy_middleware.rs +++ b/examples/examples/http_proxy_middleware.rs @@ -38,7 +38,6 @@ //! like to query a certain `URI` path for statistics. use hyper::{Body, Client, Request}; -use jsonrpsee::core::RpcResult; use std::net::SocketAddr; use std::time::Duration; @@ -95,7 +94,7 @@ async fn run_server() -> anyhow::Result { let mut module = RpcModule::new(()); module.register_method("say_hello", |_, _| "lo").unwrap(); - module.register_method("system_health", |_, _| RpcResult::Ok(serde_json::json!({ "health": true }))).unwrap(); + module.register_method("system_health", |_, _| serde_json::json!({ "health": true })).unwrap(); let handle = server.start(module)?; diff --git a/examples/examples/proc_macro.rs b/examples/examples/proc_macro.rs index 32e245d0b8..86c70cec32 100644 --- a/examples/examples/proc_macro.rs +++ b/examples/examples/proc_macro.rs @@ -26,9 +26,10 @@ use std::net::SocketAddr; -use jsonrpsee::core::{async_trait, client::Subscription, Error, SubscriptionResult}; +use jsonrpsee::core::{async_trait, client::Subscription, SubscriptionResult}; use jsonrpsee::proc_macros::rpc; use jsonrpsee::server::{PendingSubscriptionSink, ServerBuilder, SubscriptionMessage}; +use jsonrpsee::types::ErrorObjectOwned; use jsonrpsee::ws_client::WsClientBuilder; type ExampleHash = [u8; 32]; @@ -41,7 +42,11 @@ where { /// Async method call example. #[method(name = "getKeys")] - async fn storage_keys(&self, storage_key: StorageKey, hash: Option) -> Result, Error>; + async fn storage_keys( + &self, + storage_key: StorageKey, + hash: Option, + ) -> Result, ErrorObjectOwned>; /// Subscription that takes a `StorageKey` as input and produces a `Vec`. #[subscription(name = "subscribeStorage" => "override", item = Vec)] @@ -56,7 +61,7 @@ impl RpcServer for RpcServerImpl { &self, storage_key: ExampleStorageKey, _hash: Option, - ) -> Result, Error> { + ) -> Result, ErrorObjectOwned> { Ok(vec![storage_key]) } diff --git a/examples/examples/proc_macro_bounds.rs b/examples/examples/proc_macro_bounds.rs index 04ed8f2ffb..352a52ac20 100644 --- a/examples/examples/proc_macro_bounds.rs +++ b/examples/examples/proc_macro_bounds.rs @@ -26,9 +26,10 @@ use std::net::SocketAddr; -use jsonrpsee::core::{async_trait, Error}; +use jsonrpsee::core::async_trait; use jsonrpsee::proc_macros::rpc; use jsonrpsee::server::ServerBuilder; +use jsonrpsee::types::ErrorObjectOwned; use jsonrpsee::ws_client::WsClientBuilder; type ExampleHash = [u8; 32]; @@ -53,14 +54,14 @@ impl Config for ExampleHash { #[rpc(server, client, namespace = "foo", client_bounds(T::Hash: jsonrpsee::core::DeserializeOwned), server_bounds(T::Hash: jsonrpsee::core::Serialize + Clone))] pub trait Rpc { #[method(name = "bar")] - fn method(&self) -> Result; + fn method(&self) -> Result; } pub struct RpcServerImpl; #[async_trait] impl RpcServer for RpcServerImpl { - fn method(&self) -> Result<::Hash, Error> { + fn method(&self) -> Result<::Hash, ErrorObjectOwned> { Ok([0u8; 32]) } } diff --git a/examples/examples/ws_pubsub_with_params.rs b/examples/examples/ws_pubsub_with_params.rs index f6fa5754ad..7920c713e9 100644 --- a/examples/examples/ws_pubsub_with_params.rs +++ b/examples/examples/ws_pubsub_with_params.rs @@ -31,7 +31,6 @@ use futures::{Stream, StreamExt}; use jsonrpsee::core::client::{Subscription, SubscriptionClientT}; use jsonrpsee::core::Serialize; use jsonrpsee::server::{RpcModule, ServerBuilder, SubscriptionMessage, TrySendError}; -use jsonrpsee::types::ErrorObjectOwned; use jsonrpsee::ws_client::WsClientBuilder; use jsonrpsee::{rpc_params, PendingSubscriptionSink}; use tokio::time::interval; @@ -72,7 +71,7 @@ async fn run_server() -> anyhow::Result { let idx = match params.one::() { Ok(p) => p, Err(e) => { - let _ = pending.reject(ErrorObjectOwned::from(e)).await; + let _ = pending.reject(e).await; return Ok(()); } }; diff --git a/proc-macros/src/render_client.rs b/proc-macros/src/render_client.rs index 08826767da..cc366ab73f 100644 --- a/proc-macros/src/render_client.rs +++ b/proc-macros/src/render_client.rs @@ -102,7 +102,12 @@ impl RpcDescription { if args.len() != 1 { return quote_spanned!(args.span() => compile_error!("RpcResult must have one argument")); } - quote!(#ty) + + // The type alias `RpcResult` is modified to `Result`. + let ret_ty = args.last_mut().unwrap(); + let err_ty = self.jrps_client_item(quote! { core::Error }); + + quote! { core::result::Result<#ret_ty, #err_ty> } } else { // Any other type name isn't allowed. quote_spanned!(type_name.span() => compile_error!("The return type must be Result or RpcResult")) diff --git a/proc-macros/tests/ui/correct/custom_ret_types.rs b/proc-macros/tests/ui/correct/custom_ret_types.rs index 4bce2fc1e6..8851e4d5db 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, RpcResult, Serialize}; +use jsonrpsee::core::{async_trait, Serialize}; use jsonrpsee::proc_macros::rpc; use jsonrpsee::server::{IntoResponse, ServerBuilder}; use jsonrpsee::types::ResponsePayload; @@ -86,7 +86,7 @@ async fn main() { let client = WsClientBuilder::default().build(&server_url).await.unwrap(); let get_error_object = |err| match err { - jsonrpsee::core::Error::Call(jsonrpsee::types::error::CallError::Custom(object)) => object, + jsonrpsee::core::Error::Call(object) => object, _ => panic!("wrong error kind: {:?}", err), }; diff --git a/proc-macros/tests/ui/correct/only_client.rs b/proc-macros/tests/ui/correct/only_client.rs index 325dd13603..423ddc25ca 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. -use jsonrpsee::{core::RpcResult, proc_macros::rpc}; +use jsonrpsee::proc_macros::rpc; #[rpc(client)] pub trait Rpc { #[method(name = "foo")] - async fn async_method(&self, param_a: u8, param_b: String) -> RpcResult; + async fn async_method(&self, param_a: u8, param_b: String) -> Result; #[method(name = "bar")] - fn sync_method(&self) -> RpcResult; + fn sync_method(&self) -> Result; #[subscription(name = "subscribe", item = String)] async fn sub(&self) -> Result<(), Error>; diff --git a/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.rs b/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.rs index 921fa3ed8d..1da2a96382 100644 --- a/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.rs +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.rs @@ -1,5 +1,4 @@ -use jsonrpsee::core::Error; -use jsonrpsee::proc_macros::rpc; +use jsonrpsee::{core::RpcResult, proc_macros::rpc}; pub trait Config { type Hash: Send + Sync + 'static; @@ -10,7 +9,7 @@ pub trait Config { #[rpc(server, client, namespace = "foo", client_bounds(), server_bounds())] pub trait EmptyBounds { #[method(name = "bar")] - fn method(&self) -> Result; + fn method(&self) -> RpcResult; } 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 index 06584f2faa..e1c9de4a3f 100644 --- a/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.stderr +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.stderr @@ -1,41 +1,41 @@ 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 for `std::result::Result<::Hash, jsonrpsee::jsonrpsee_core::Error>` to implement `IntoResponse` + --> tests/ui/incorrect/rpc/rpc_empty_bounds.rs:9:1 + | +9 | #[rpc(server, client, namespace = "foo", client_bounds(), server_bounds())] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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) + --> $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 - --> tests/ui/incorrect/rpc/rpc_empty_bounds.rs:10:1 - | -10 | #[rpc(server, client, namespace = "foo", client_bounds(), server_bounds())] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `::Hash` - | - = note: required for `std::result::Result<::Hash, jsonrpsee::jsonrpsee_core::Error>` to implement `IntoResponse` + --> tests/ui/incorrect/rpc/rpc_empty_bounds.rs:9:1 + | +9 | #[rpc(server, client, namespace = "foo", client_bounds(), server_bounds())] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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) + --> $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 - --> 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 for `::Hash` to implement `DeserializeOwned` + --> tests/ui/incorrect/rpc/rpc_empty_bounds.rs:9:1 + | +9 | #[rpc(server, client, namespace = "foo", client_bounds(), server_bounds())] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'de> Deserialize<'de>` is not implemented for `::Hash` + | + = note: required for `::Hash` to implement `DeserializeOwned` note: required by a bound in `request` - --> $WORKSPACE/core/src/client/mod.rs - | - | R: DeserializeOwned, - | ^^^^^^^^^^^^^^^^ required by this bound in `ClientT::request` - = note: this error originates in the attribute macro `rpc` (in Nightly builds, run with -Z macro-backtrace for more info) + --> $WORKSPACE/core/src/client/mod.rs + | + | R: DeserializeOwned, + | ^^^^^^^^^^^^^^^^ required by this bound in `ClientT::request` + = note: this error originates in the attribute macro `rpc` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/server/src/tests/helpers.rs b/server/src/tests/helpers.rs index b62899dd18..46c2144c8d 100644 --- a/server/src/tests/helpers.rs +++ b/server/src/tests/helpers.rs @@ -1,16 +1,24 @@ use std::fmt; use std::net::SocketAddr; -use crate::types::error::CallError; use crate::{RpcModule, ServerBuilder, ServerHandle}; -use anyhow::anyhow; -use jsonrpsee_core::{DeserializeOwned, Error, RpcResult, StringError}; -use jsonrpsee_test_utils::mocks::TestContext; +use jsonrpsee_core::{DeserializeOwned, RpcResult, StringError}; use jsonrpsee_test_utils::TimeoutFutureExt; -use jsonrpsee_types::{Response, ResponseSuccess}; +use jsonrpsee_types::{error::ErrorCode, ErrorObject, ErrorObjectOwned, Response, ResponseSuccess}; use tracing_subscriber::{EnvFilter, FmtSubscriber}; +pub(crate) struct TestContext; + +impl TestContext { + pub(crate) fn ok(&self) -> Result<(), MyAppError> { + Ok(()) + } + pub(crate) fn err(&self) -> Result<(), MyAppError> { + Err(MyAppError) + } +} + /// Spawns a dummy JSON-RPC server. pub(crate) async fn server() -> SocketAddr { let (addr, handle) = server_with_handles().await; @@ -20,14 +28,6 @@ pub(crate) async fn server() -> SocketAddr { /// Spawns a dummy JSON-RPC server. /// -/// It has the following methods: -/// sync methods: `say_hello` and `add` -/// async: `say_hello_async` and `add_sync` -/// other: `invalid_params` (always returns `CallError::InvalidParams`), -/// `call_fail` (always returns `CallError::Failed`), -/// `sleep_for` -/// `subscribe_hello` (starts a subscription that doesn't send anything) -/// /// Returns the address together with handle for the server. pub(crate) async fn server_with_handles() -> (SocketAddr, ServerHandle) { let server = ServerBuilder::default().build("127.0.0.1:0").with_default_timeout().await.unwrap().unwrap(); @@ -40,17 +40,17 @@ pub(crate) async fn server_with_handles() -> (SocketAddr, ServerHandle) { }) .unwrap(); module - .register_method("add", |params, _| { + .register_method::, _>("add", |params, _| { let params: Vec = params.parse()?; let sum: u64 = params.into_iter().sum(); - RpcResult::Ok(sum) + Ok(sum) }) .unwrap(); module - .register_method("multiparam", |params, _| { + .register_method::, _>("multiparam", |params, _| { let params: (String, String, Vec) = params.parse()?; let r = format!("string1={}, string2={}, vec={}", params.0.len(), params.1.len(), params.2.len()); - RpcResult::Ok(r) + Ok(r) }) .unwrap(); module @@ -64,23 +64,19 @@ pub(crate) async fn server_with_handles() -> (SocketAddr, ServerHandle) { }) .unwrap(); module - .register_async_method("add_async", |params, _| async move { + .register_async_method::, _, _>("add_async", |params, _| async move { let params: Vec = params.parse()?; let sum: u64 = params.into_iter().sum(); - RpcResult::Ok(sum) - }) - .unwrap(); - module - .register_method::, _>("invalid_params", |_params, _| { - Err(CallError::InvalidParams(anyhow!("buh!")).into()) + Ok(sum) }) .unwrap(); - module.register_method("call_fail", |_params, _| Err::<(), _>(Error::to_call_error(MyAppError))).unwrap(); + module.register_method("invalid_params", |_params, _| Err::<(), _>(invalid_params())).unwrap(); + module.register_method("call_fail", |_params, _| Err::<(), _>(MyAppError)).unwrap(); module - .register_method("sleep_for", |params, _| { + .register_method::, _>("sleep_for", |params, _| { let sleep: Vec = params.parse()?; std::thread::sleep(std::time::Duration::from_millis(sleep[0])); - RpcResult::Ok("Yawn!") + Ok("Yawn!") }) .unwrap(); module @@ -102,21 +98,21 @@ pub(crate) async fn server_with_handles() -> (SocketAddr, ServerHandle) { module.register_method("notif", |_, _| "").unwrap(); module .register_method("should_err", |_, ctx| { - ctx.err().map_err(CallError::Failed)?; + ctx.err()?; RpcResult::Ok("err") }) .unwrap(); module .register_method("should_ok", |_, ctx| { - ctx.ok().map_err(CallError::Failed)?; + ctx.ok()?; RpcResult::Ok("ok") }) .unwrap(); module .register_async_method("should_ok_async", |_p, ctx| async move { - ctx.ok().map_err(CallError::Failed)?; - Result::<_, Error>::Ok("ok") + ctx.ok()?; + Ok::<_, MyAppError>("ok") }) .unwrap(); @@ -135,31 +131,31 @@ pub(crate) async fn server_with_context() -> SocketAddr { rpc_module .register_method("should_err", |_p, ctx| { - ctx.err().map_err(CallError::Failed)?; + ctx.err()?; RpcResult::Ok("err") }) .unwrap(); rpc_module .register_method("should_ok", |_p, ctx| { - ctx.ok().map_err(CallError::Failed)?; + ctx.ok()?; RpcResult::Ok("ok") }) .unwrap(); rpc_module .register_async_method("should_ok_async", |_p, ctx| async move { - ctx.ok().map_err(CallError::Failed)?; + ctx.ok()?; // Call some async function inside. - Result::<_, Error>::Ok(futures_util::future::ready("ok!").await) + Result::<_, MyAppError>::Ok(futures_util::future::ready("ok!").await) }) .unwrap(); rpc_module .register_async_method("err_async", |_p, ctx| async move { - ctx.ok().map_err(CallError::Failed)?; + ctx.ok()?; // Async work that returns an error - futures_util::future::err::<(), Error>(anyhow!("nah").into()).await + futures_util::future::err::<(), _>(MyAppError).await }) .unwrap(); @@ -180,11 +176,21 @@ pub(crate) fn deser_call(raw: String) } /// Applications can/should provide their own error. -#[derive(Debug)] -struct MyAppError; +#[derive(Copy, Clone, Debug)] +pub struct MyAppError; impl fmt::Display for MyAppError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "MyAppError") } } impl std::error::Error for MyAppError {} + +impl From for ErrorObjectOwned { + fn from(_: MyAppError) -> Self { + ErrorObject::owned(-32000, "MyAppError", None::<()>) + } +} + +fn invalid_params() -> ErrorObjectOwned { + ErrorCode::InvalidParams.into() +} diff --git a/server/src/tests/http.rs b/server/src/tests/http.rs index 75db973562..32a8aa846e 100644 --- a/server/src/tests/http.rs +++ b/server/src/tests/http.rs @@ -27,14 +27,16 @@ use std::net::SocketAddr; use crate::server::BatchRequestConfig; -use crate::types::error::CallError; use crate::{RpcModule, ServerBuilder, ServerHandle}; use jsonrpsee_core::{Error, RpcResult}; use jsonrpsee_test_utils::helpers::*; -use jsonrpsee_test_utils::mocks::{Id, StatusCode, TestContext}; +use jsonrpsee_test_utils::mocks::{Id, StatusCode}; use jsonrpsee_test_utils::TimeoutFutureExt; +use jsonrpsee_types::ErrorObjectOwned; use serde_json::Value as JsonValue; +use super::helpers::{MyAppError, TestContext}; + fn init_logger() { let _ = tracing_subscriber::FmtSubscriber::builder() .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) @@ -46,8 +48,8 @@ async fn server() -> (SocketAddr, ServerHandle) { let ctx = TestContext; let mut module = RpcModule::new(ctx); let addr = server.local_addr().unwrap(); - module.register_method("say_hello", |_, _| RpcResult::Ok("lo")).unwrap(); - module.register_async_method("say_hello_async", |_, _| async move { Result::<_, Error>::Ok("lo") }).unwrap(); + module.register_method("say_hello", |_, _| "lo").unwrap(); + module.register_async_method("say_hello_async", |_, _| async move { RpcResult::Ok("lo") }).unwrap(); module .register_method("add", |params, _| { let params: Vec = params.parse()?; @@ -56,30 +58,30 @@ async fn server() -> (SocketAddr, ServerHandle) { }) .unwrap(); module - .register_method("multiparam", |params, _| { + .register_method::, _>("multiparam", |params, _| { let params: (String, String, Vec) = params.parse()?; let r = format!("string1={}, string2={}, vec={}", params.0.len(), params.1.len(), params.2.len()); - RpcResult::Ok(r) + Ok(r) }) .unwrap(); module.register_method("notif", |_, _| "").unwrap(); module .register_method("should_err", |_, ctx| { - ctx.err().map_err(CallError::Failed)?; - RpcResult::Ok("err") + ctx.err()?; + Ok::<_, MyAppError>("err") }) .unwrap(); module .register_method("should_ok", |_, ctx| { - ctx.ok().map_err(CallError::Failed)?; - RpcResult::Ok("ok") + ctx.ok()?; + Ok::<_, MyAppError>("ok") }) .unwrap(); module .register_async_method("should_ok_async", |_p, ctx| async move { - ctx.ok().map_err(CallError::Failed)?; - RpcResult::Ok("ok") + ctx.ok()?; + Ok::<_, MyAppError>("ok") }) .unwrap(); @@ -153,7 +155,7 @@ async fn single_method_call_with_multiple_params_of_different_types() { async fn single_method_call_with_faulty_params_returns_err() { let (addr, _handle) = server().with_default_timeout().await.unwrap(); let uri = to_http_uri(addr); - let expected = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"invalid type: string \"this should be a number\", expected u64 at line 1 column 26"},"id":1}"#; + let expected = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid params","data":"invalid type: string \"this should be a number\", expected u64 at line 1 column 26"},"id":1}"#; let req = r#"{"jsonrpc":"2.0","method":"add", "params":["this should be a number"],"id":1}"#; let response = http_request(req.into(), uri).with_default_timeout().await.unwrap().unwrap(); @@ -169,7 +171,7 @@ async fn single_method_call_with_faulty_context() { let req = r#"{"jsonrpc":"2.0","method":"should_err","params":[],"id":1}"#; let response = http_request(req.into(), uri).with_default_timeout().await.unwrap().unwrap(); assert_eq!(response.status, StatusCode::OK); - assert_eq!(response.body, call_execution_failed("RPC context failed", Id::Num(1))); + assert_eq!(response.body, call_execution_failed("MyAppError", Id::Num(1))); } #[tokio::test] diff --git a/server/src/tests/ws.rs b/server/src/tests/ws.rs index 01009db110..e77e7dffe0 100644 --- a/server/src/tests/ws.rs +++ b/server/src/tests/ws.rs @@ -29,7 +29,6 @@ use crate::tests::helpers::{deser_call, init_logger, server_with_context}; use crate::types::SubscriptionId; use crate::{RpcModule, ServerBuilder}; use jsonrpsee_core::server::{SendTimeoutError, SubscriptionMessage}; -use jsonrpsee_core::RpcResult; use jsonrpsee_core::{traits::IdProvider, Error}; use jsonrpsee_test_utils::helpers::*; use jsonrpsee_test_utils::mocks::{Id, WebSocketTestClient, WebSocketTestError}; @@ -121,7 +120,7 @@ async fn can_set_max_connections() { // Server that accepts max 2 connections let server = ServerBuilder::default().max_connections(2).build(addr).await.unwrap(); let mut module = RpcModule::new(()); - module.register_method("anything", |_p, _cx| RpcResult::Ok(())).unwrap(); + module.register_method("anything", |_p, _cx| ()).unwrap(); let addr = server.local_addr().unwrap(); let server_handle = server.start(module).unwrap(); @@ -303,7 +302,7 @@ async fn single_method_call_with_params_works() { async fn single_method_call_with_faulty_params_returns_err() { let addr = server().await; let mut client = WebSocketTestClient::new(addr).with_default_timeout().await.unwrap().unwrap(); - let expected = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"invalid type: string \"should be a number\", expected u64 at line 1 column 21"},"id":1}"#; + let expected = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid params","data":"invalid type: string \"should be a number\", expected u64 at line 1 column 21"},"id":1}"#; let req = r#"{"jsonrpc":"2.0","method":"add", "params":["should be a number"],"id":1}"#; let response = client.send_request_text(req).with_default_timeout().await.unwrap().unwrap(); @@ -317,7 +316,7 @@ async fn single_method_call_with_faulty_context() { let req = r#"{"jsonrpc":"2.0","method":"should_err", "params":[],"id":1}"#; let response = client.send_request_text(req).with_default_timeout().await.unwrap().unwrap(); - assert_eq!(response, call_execution_failed("RPC context failed", Id::Num(1))); + assert_eq!(response, call_execution_failed("MyAppError", Id::Num(1))); } #[tokio::test] @@ -357,7 +356,7 @@ async fn async_method_call_that_fails() { let req = r#"{"jsonrpc":"2.0","method":"err_async", "params":[],"id":1}"#; let response = client.send_request_text(req).await.unwrap(); - assert_eq!(response, call_execution_failed("nah", Id::Num(1))); + assert_eq!(response, call_execution_failed("MyAppError", Id::Num(1))); } #[tokio::test] diff --git a/test-utils/Cargo.toml b/test-utils/Cargo.toml index b86c96050c..d998976b21 100644 --- a/test-utils/Cargo.toml +++ b/test-utils/Cargo.toml @@ -7,7 +7,6 @@ license.workspace = true publish = false [dependencies] -anyhow = "1" futures-channel = "0.3.14" futures-util = "0.3.14" hyper = { version = "0.14.10", features = ["full"] } diff --git a/test-utils/src/mocks.rs b/test-utils/src/mocks.rs index e5bdb4a806..7f19f5538a 100644 --- a/test-utils/src/mocks.rs +++ b/test-utils/src/mocks.rs @@ -44,17 +44,6 @@ pub use hyper::{Body, HeaderMap, StatusCode, Uri}; type Error = Box; -pub struct TestContext; - -impl TestContext { - pub fn ok(&self) -> Result<(), anyhow::Error> { - Ok(()) - } - pub fn err(&self) -> Result<(), anyhow::Error> { - Err(anyhow::anyhow!("RPC context failed")) - } -} - /// Request Id #[derive(Debug, PartialEq, Clone, Hash, Eq, Deserialize, Serialize)] #[serde(deny_unknown_fields)] @@ -344,7 +333,7 @@ pub fn ws_server_with_redirect(other_server: String) -> String { let server = hyper::Server::bind(&addr).serve(service); let addr = server.local_addr(); - tokio::spawn(async move { server.await }); + tokio::spawn(server); format!("ws://{addr}") } diff --git a/tests/tests/helpers.rs b/tests/tests/helpers.rs index 353775acb9..7992e82c7a 100644 --- a/tests/tests/helpers.rs +++ b/tests/tests/helpers.rs @@ -30,12 +30,12 @@ use std::net::SocketAddr; use std::time::Duration; use futures::{SinkExt, Stream, StreamExt}; -use jsonrpsee::core::{Error, RpcResult}; +use jsonrpsee::core::Error; use jsonrpsee::server::middleware::proxy_get_request::ProxyGetRequestLayer; use jsonrpsee::server::{ AllowHosts, PendingSubscriptionSink, RpcModule, ServerBuilder, ServerHandle, SubscriptionMessage, TrySendError, }; -use jsonrpsee::types::ErrorObjectOwned; +use jsonrpsee::types::{ErrorObject, ErrorObjectOwned}; use jsonrpsee::{IntoSubscriptionCloseResponse, SubscriptionCloseResponse}; use serde::Serialize; use tokio::time::interval; @@ -74,7 +74,7 @@ pub async fn server_with_subscription_and_handle() -> (SocketAddr, ServerHandle) let count = match params.one::().map(|c| c.wrapping_add(1)) { Ok(count) => count, Err(e) => { - let _ = pending.reject(ErrorObjectOwned::from(e)).await; + let _ = pending.reject(e).await; return Ok(()); } }; @@ -159,13 +159,19 @@ pub async fn server() -> SocketAddr { module .register_async_method("slow_hello", |_, _| async { tokio::time::sleep(std::time::Duration::from_secs(1)).await; - Result::<_, Error>::Ok("hello") + "hello" }) .unwrap(); - module - .register_async_method::, _, _>("err", |_, _| async { Err(Error::Custom("err".to_string())) }) - .unwrap(); + struct CustomError; + + impl From for ErrorObjectOwned { + fn from(_: CustomError) -> Self { + ErrorObject::owned(-32001, "err", None::<()>) + } + } + + module.register_async_method::, _, _>("err", |_, _| async { Err(CustomError) }).unwrap(); let addr = server.local_addr().unwrap(); @@ -227,7 +233,7 @@ pub async fn server_with_access_control(allowed_hosts: AllowHosts, cors: CorsLay module.register_method("say_hello", |_, _| "hello").unwrap(); module.register_method("notif", |_, _| "").unwrap(); - module.register_method("system_health", |_, _| RpcResult::Ok(serde_json::json!({ "health": true }))).unwrap(); + module.register_method("system_health", |_, _| serde_json::json!({ "health": true })).unwrap(); let handle = server.start(module).unwrap(); (addr, handle) diff --git a/tests/tests/integration_tests.rs b/tests/tests/integration_tests.rs index e39d571015..c7d0b0828a 100644 --- a/tests/tests/integration_tests.rs +++ b/tests/tests/integration_tests.rs @@ -690,7 +690,7 @@ async fn ws_batch_works() { }) .collect(); assert_eq!(ok_responses, vec!["hello"]); - assert_eq!(err_responses, vec![&ErrorObject::borrowed(UNKNOWN_ERROR_CODE, &"Custom error: err", None)]); + assert_eq!(err_responses, vec![&ErrorObject::borrowed(UNKNOWN_ERROR_CODE, &"err", None)]); } #[tokio::test] @@ -730,13 +730,13 @@ async fn http_batch_works() { }) .collect(); assert_eq!(ok_responses, vec!["hello"]); - assert_eq!(err_responses, vec![&ErrorObject::borrowed(UNKNOWN_ERROR_CODE, &"Custom error: err", None)]); + assert_eq!(err_responses, vec![&ErrorObject::borrowed(UNKNOWN_ERROR_CODE, &"err", None)]); } #[tokio::test] async fn ws_server_limit_subs_per_conn_works() { use futures::StreamExt; - use jsonrpsee::types::error::{CallError, TOO_MANY_SUBSCRIPTIONS_CODE, TOO_MANY_SUBSCRIPTIONS_MSG}; + use jsonrpsee::types::error::{TOO_MANY_SUBSCRIPTIONS_CODE, TOO_MANY_SUBSCRIPTIONS_MSG}; use jsonrpsee::{server::ServerBuilder, RpcModule}; init_logger(); @@ -781,10 +781,10 @@ async fn ws_server_limit_subs_per_conn_works() { let data = "\"Exceeded max limit of 10\""; assert!( - matches!(err1, Err(Error::Call(CallError::Custom(err))) if err.code() == TOO_MANY_SUBSCRIPTIONS_CODE && err.message() == TOO_MANY_SUBSCRIPTIONS_MSG && err.data().unwrap().get() == data) + matches!(err1, Err(Error::Call(err)) if err.code() == TOO_MANY_SUBSCRIPTIONS_CODE && err.message() == TOO_MANY_SUBSCRIPTIONS_MSG && err.data().unwrap().get() == data) ); assert!( - matches!(err2, Err(Error::Call(CallError::Custom(err))) if err.code() == TOO_MANY_SUBSCRIPTIONS_CODE && err.message() == TOO_MANY_SUBSCRIPTIONS_MSG && err.data().unwrap().get() == data) + matches!(err2, Err(Error::Call(err)) if err.code() == TOO_MANY_SUBSCRIPTIONS_CODE && err.message() == TOO_MANY_SUBSCRIPTIONS_MSG && err.data().unwrap().get() == data) ); } diff --git a/tests/tests/metrics.rs b/tests/tests/metrics.rs index c5d7e32afa..a07a1d69b1 100644 --- a/tests/tests/metrics.rs +++ b/tests/tests/metrics.rs @@ -101,9 +101,9 @@ fn test_module() -> RpcModule<()> { #[rpc(server)] pub trait Rpc { #[method(name = "say_hello")] - async fn hello(&self) -> Result<&'static str, Error> { + async fn hello(&self) -> String { sleep(Duration::from_millis(50)).await; - Ok("hello") + "hello".to_string() } } diff --git a/tests/tests/proc_macros.rs b/tests/tests/proc_macros.rs index 9c43cacede..5932672028 100644 --- a/tests/tests/proc_macros.rs +++ b/tests/tests/proc_macros.rs @@ -36,7 +36,7 @@ use jsonrpsee::core::{client::SubscriptionClientT, Error}; use jsonrpsee::http_client::HttpClientBuilder; use jsonrpsee::rpc_params; use jsonrpsee::server::ServerBuilder; -use jsonrpsee::types::error::{CallError, ErrorCode}; +use jsonrpsee::types::error::{ErrorCode, INVALID_PARAMS_MSG}; use jsonrpsee::ws_client::*; use serde_json::json; @@ -45,8 +45,9 @@ mod rpc_impl { use jsonrpsee::core::server::{ IntoSubscriptionCloseResponse, PendingSubscriptionSink, SubscriptionCloseResponse, SubscriptionMessage, }; - use jsonrpsee::core::{async_trait, RpcResult, SubscriptionResult}; + use jsonrpsee::core::{async_trait, SubscriptionResult}; use jsonrpsee::proc_macros::rpc; + use jsonrpsee::types::ErrorObjectOwned; pub struct CustomSubscriptionRet; @@ -59,10 +60,10 @@ mod rpc_impl { #[rpc(client, server, namespace = "foo")] pub trait Rpc { #[method(name = "foo")] - async fn async_method(&self, param_a: u8, param_b: String) -> RpcResult; + async fn async_method(&self, param_a: u8, param_b: String) -> Result; #[method(name = "bar")] - fn sync_method(&self) -> RpcResult; + fn sync_method(&self) -> Result; #[subscription(name = "sub", unsubscribe = "unsub", item = String)] async fn sub(&self) -> SubscriptionResult; @@ -79,12 +80,12 @@ mod rpc_impl { async fn sub_unit_type(&self, x: usize); #[method(name = "params")] - fn params(&self, a: u8, b: &str) -> RpcResult { + fn params(&self, a: u8, b: &str) -> Result { Ok(format!("Called with: {}, {}", a, b)) } #[method(name = "optional_params")] - fn optional_params(&self, a: u32, b: Option, c: Option) -> RpcResult { + fn optional_params(&self, a: u32, b: Option, c: Option) -> Result { Ok(format!("Called with: {}, {:?}, {:?}", a, b, c)) } @@ -95,17 +96,21 @@ mod rpc_impl { b: &'_ str, c: std::borrow::Cow<'_, str>, d: Option>, - ) -> RpcResult { + ) -> Result { 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>) -> RpcResult { + fn zero_copy_cow( + &self, + a: std::borrow::Cow<'_, str>, + b: beef::Cow<'_, str>, + ) -> Result { Ok(format!("Zero copy params: {}, {}", matches!(a, std::borrow::Cow::Borrowed(_)), b.is_borrowed())) } #[method(name = "blocking_call", blocking)] - fn blocking_call(&self) -> RpcResult { + fn blocking_call(&self) -> Result { std::thread::sleep(std::time::Duration::from_millis(50)); Ok(42) } @@ -115,12 +120,12 @@ mod rpc_impl { #[async_trait] impl RpcServer for RpcServerImpl { - async fn async_method(&self, _param_a: u8, _param_b: String) -> RpcResult { - Ok(42u16) + async fn async_method(&self, _param_a: u8, _param_b: String) -> Result { + Ok(42) } - fn sync_method(&self) -> RpcResult { - Ok(10u16) + fn sync_method(&self) -> Result { + Ok(10) } async fn sub(&self, pending: PendingSubscriptionSink) -> SubscriptionResult { @@ -308,13 +313,15 @@ async fn calls_with_bad_params() { .unwrap_err(); assert!( - matches!(err, Error::Call(CallError::Custom (err)) if err.message().contains("invalid type: string \"0x0\", expected u32") && err.code() == ErrorCode::InvalidParams.code()) + matches!(err, Error::Call(e) if e.data().unwrap().get().contains("invalid type: string \\\"0x0\\\", expected u32") && e.code() == ErrorCode::InvalidParams.code() + && e.message() == INVALID_PARAMS_MSG) ); // Call with faulty params as array. let err: Error = client.request::("foo_foo", rpc_params!["faulty", "ok"]).await.unwrap_err(); assert!( - matches!(err, Error::Call(CallError::Custom (err)) if err.message().contains("invalid type: string \"faulty\", expected u8") && err.code() == ErrorCode::InvalidParams.code()) + matches!(err, Error::Call(e) if e.data().unwrap().get().contains("invalid type: string \\\"faulty\\\", expected u8") && e.code() == ErrorCode::InvalidParams.code() + && e.message() == INVALID_PARAMS_MSG) ); // Sub with faulty params as map. @@ -324,7 +331,9 @@ async fn calls_with_bad_params() { let err: Error = client.subscribe::("foo_echo", params, "foo_unsubscribe_echo").await.unwrap_err(); assert!( - matches!(err, Error::Call(CallError::Custom (err)) if err.message().contains("invalid type: string \"0x0\", expected u32") && err.code() == ErrorCode::InvalidParams.code()) + matches!(err, Error::Call(e) if e.data().unwrap().get().contains("invalid type: string \\\"0x0\\\", expected u32") && e.code() == ErrorCode::InvalidParams.code() + && e.message() == INVALID_PARAMS_MSG + ) ); // Call with faulty params as map. @@ -333,8 +342,11 @@ async fn calls_with_bad_params() { params.insert("param_b", 2).unwrap(); let err: Error = client.request::("foo_foo", params).await.unwrap_err(); + assert!( - matches!(err, Error::Call(CallError::Custom (err)) if err.message().contains("invalid type: integer `2`, expected a string") && err.code() == ErrorCode::InvalidParams.code()) + matches!(err, Error::Call(e) if e.data().unwrap().get().contains("invalid type: integer `2`, expected a string") && e.code() == ErrorCode::InvalidParams.code() + && e.message() == INVALID_PARAMS_MSG + ) ); } diff --git a/tests/tests/rpc_module.rs b/tests/tests/rpc_module.rs index 34de221240..a58934be28 100644 --- a/tests/tests/rpc_module.rs +++ b/tests/tests/rpc_module.rs @@ -34,7 +34,7 @@ use helpers::{init_logger, pipe_from_stream_and_drop}; use jsonrpsee::core::error::Error; use jsonrpsee::core::EmptyServerParams; use jsonrpsee::core::{server::*, RpcResult}; -use jsonrpsee::types::error::{CallError, ErrorCode, ErrorObject, PARSE_ERROR_CODE}; +use jsonrpsee::types::error::{ErrorCode, ErrorObject, INVALID_PARAMS_MSG, PARSE_ERROR_CODE}; use jsonrpsee::types::{ErrorObjectOwned, Params, Response, ResponsePayload}; use jsonrpsee::SubscriptionMessage; use serde::{Deserialize, Serialize}; @@ -54,9 +54,9 @@ macro_rules! assert_type { fn rpc_modules_with_different_contexts_can_be_merged() { let cx = Vec::::new(); let mut mod1 = RpcModule::new(cx); - mod1.register_method("bla with Vec context", |_: Params, _| RpcResult::Ok(())).unwrap(); + mod1.register_method("bla with Vec context", |_: Params, _| ()).unwrap(); let mut mod2 = RpcModule::new(String::new()); - mod2.register_method("bla with String context", |_: Params, _| RpcResult::Ok(())).unwrap(); + mod2.register_method("bla with String context", |_: Params, _| ()).unwrap(); mod1.merge(mod2).unwrap(); @@ -103,9 +103,9 @@ async fn calling_method_without_server() { // Call sync method with params module - .register_method("foo", |params, _| { + .register_method::, _>("foo", |params, _| { let n: u16 = params.one()?; - RpcResult::Ok(n * 2) + Ok(n * 2) }) .unwrap(); let res: u64 = module.call("foo", [3_u64]).await.unwrap(); @@ -115,7 +115,7 @@ async fn calling_method_without_server() { let err = module.call::<_, EmptyServerParams>("foo", (false,)).await.unwrap_err(); assert!(matches!( err, - Error::Call(CallError::Custom(err)) if err.code() == -32602 && err.message() == "invalid type: boolean `false`, expected u16 at line 1 column 6" + Error::Call(err) if err.code() == ErrorCode::InvalidParams.code() && err.message() == INVALID_PARAMS_MSG && err.data().unwrap().get().contains("invalid type: boolean `false`, expected u16 at line 1 column 6") )); // Call async method with params and context @@ -156,42 +156,42 @@ async fn calling_method_without_server_using_proc_macro() { pub trait Cool { /// Sync method, no params. #[method(name = "rebel_without_cause")] - fn rebel_without_cause(&self) -> Result; + fn rebel_without_cause(&self) -> RpcResult; /// Sync method. #[method(name = "rebel")] - fn rebel(&self, gun: Gun, map: HashMap) -> Result; + fn rebel(&self, gun: Gun, map: HashMap) -> RpcResult; /// Async method. #[method(name = "revolution")] - async fn can_have_any_name(&self, beverage: Beverage, some_bytes: Vec) -> Result; + async fn can_have_any_name(&self, beverage: Beverage, some_bytes: Vec) -> RpcResult; /// Async method with option. #[method(name = "can_have_options")] - async fn can_have_options(&self, x: usize) -> Result, Error>; + async fn can_have_options(&self, x: usize) -> RpcResult>; } struct CoolServerImpl; #[async_trait] impl CoolServer for CoolServerImpl { - fn rebel_without_cause(&self) -> Result { + fn rebel_without_cause(&self) -> RpcResult { Ok(false) } - fn rebel(&self, gun: Gun, map: HashMap) -> Result { + fn rebel(&self, gun: Gun, map: HashMap) -> RpcResult { Ok(format!("{} {:?}", map.values().len(), gun)) } - async fn can_have_any_name(&self, beverage: Beverage, some_bytes: Vec) -> Result { + async fn can_have_any_name(&self, beverage: Beverage, some_bytes: Vec) -> RpcResult { Ok(format!("drink: {:?}, phases: {:?}", beverage, some_bytes)) } - async fn can_have_options(&self, x: usize) -> Result, Error> { + async fn can_have_options(&self, x: usize) -> RpcResult> { match x { 0 => Ok(Some("one".to_string())), 1 => Ok(None), - _ => Err(Error::Custom("too big number".to_string())), + _ => Err(ErrorObject::owned(1, "too big number".to_string(), None::<()>)), } } } @@ -207,8 +207,10 @@ async fn calling_method_without_server_using_proc_macro() { // Call sync method with bad params let err = module.call::<_, EmptyServerParams>("rebel", (Gun { shoots: true }, false)).await.unwrap_err(); + assert!(matches!(err, - Error::Call(CallError::Custom(err)) if err.code() == -32602 && err.message() == "invalid type: boolean `false`, expected a map at line 1 column 5" + Error::Call(err) if err.data().unwrap().get().contains("invalid type: boolean `false`, expected a map at line 1 column 5") && + err.code() == ErrorCode::InvalidParams.code() && err.message() == INVALID_PARAMS_MSG )); // Call async method with params and context @@ -226,7 +228,7 @@ async fn calling_method_without_server_using_proc_macro() { // Call async method with option which should `Err`. let err = module.call::<_, Option>("can_have_options", vec![2]).await.unwrap_err(); assert!(matches!(err, - Error::Call(CallError::Custom(err)) if err.message() == "Custom error: too big number" + Error::Call(err) if err.message() == "too big number" )); } @@ -244,7 +246,7 @@ async fn subscribing_without_server() { while let Some(letter) = stream_data.pop() { tracing::debug!("This is your friendly subscription sending data."); let msg = SubscriptionMessage::from_json(&letter).unwrap(); - let _ = sink.send(msg).await.unwrap(); + sink.send(msg).await.unwrap(); tokio::time::sleep(std::time::Duration::from_millis(500)).await; } @@ -277,10 +279,8 @@ async fn close_test_subscribing_without_server() { sink.send(msg.clone()).await?; sink.closed().await; - match sink.send(msg).await { - Ok(_) => panic!("The sink should be closed"), - Err(DisconnectError(_)) => {} - } + sink.send(msg).await.expect("The sink should be closed"); + Ok(()) }) .unwrap(); @@ -318,8 +318,7 @@ async fn subscribing_without_server_bad_params() { let p = match params.one::() { Ok(p) => p, Err(e) => { - let err: ErrorObjectOwned = e.into(); - let _ = pending.reject(err).await; + let _ = pending.reject(e).await; return Ok(()); } }; @@ -335,7 +334,9 @@ async fn subscribing_without_server_bad_params() { let sub = module.subscribe_unbounded("my_sub", EmptyServerParams::new()).await.unwrap_err(); assert!( - matches!(sub, Error::Call(CallError::Custom(e)) if e.message().contains("invalid length 0, expected an array of length 1 at line 1 column 2") && e.code() == ErrorCode::InvalidParams.code()) + matches!(sub, Error::Call(e) if e.data().unwrap().get().contains("invalid length 0, expected an array of length 1 at line 1 column 2") && e.code() == ErrorCode::InvalidParams.code() + && e.message() == INVALID_PARAMS_MSG + ) ); } @@ -409,9 +410,7 @@ async fn rejected_subscription_without_server() { .unwrap(); let sub_err = module.subscribe_unbounded("my_sub", EmptyServerParams::new()).await.unwrap_err(); - assert!( - matches!(sub_err, Error::Call(CallError::Custom(e)) if e.message().contains("rejected") && e.code() == PARSE_ERROR_CODE) - ); + assert!(matches!(sub_err, Error::Call(e) if e.message().contains("rejected") && e.code() == PARSE_ERROR_CODE)); } #[tokio::test] diff --git a/types/src/error.rs b/types/src/error.rs index c7e1a2433c..cb2315ce41 100644 --- a/types/src/error.rs +++ b/types/src/error.rs @@ -37,8 +37,9 @@ use thiserror::Error; pub type ErrorObjectOwned = ErrorObject<'static>; /// [Failed JSON-RPC response object](https://www.jsonrpc.org/specification#response_object). -#[derive(Debug, Deserialize, Serialize, Clone)] +#[derive(Debug, Deserialize, Serialize, Clone, thiserror::Error)] #[serde(deny_unknown_fields)] +#[error("{self:?}")] pub struct ErrorObject<'a> { /// Code code: ErrorCode, @@ -109,16 +110,6 @@ impl<'a> From for ErrorObject<'a> { } } -impl<'a> From for ErrorObject<'a> { - fn from(error: CallError) -> Self { - match error { - CallError::InvalidParams(e) => ErrorObject::owned(INVALID_PARAMS_CODE, e.to_string(), None::<()>), - CallError::Failed(e) => ErrorObject::owned(CALL_EXECUTION_FAILED_CODE, e.to_string(), None::<()>), - CallError::Custom(err) => err, - } - } -} - /// Parse error code. pub const PARSE_ERROR_CODE: i32 = -32700; /// Invalid request error code. @@ -265,30 +256,6 @@ impl serde::Serialize for ErrorCode { } } -/// Error that occurs when a call failed. -#[derive(Debug, thiserror::Error)] -pub enum CallError { - /// Invalid params in the call. - #[error("Invalid params in the call: {0}")] - InvalidParams(#[source] anyhow::Error), - /// The call failed (let jsonrpsee assign default error code and error message). - #[error("RPC call failed: {0}")] - Failed(#[from] anyhow::Error), - /// Custom error with specific JSON-RPC error code, message and data. - #[error("RPC call failed: {0:?}")] - Custom(ErrorObjectOwned), -} - -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()) - } -} - /// Helper to get a `JSON-RPC` error object when the maximum number of subscriptions have been exceeded. pub fn reject_too_many_subscriptions(limit: u32) -> ErrorObjectOwned { ErrorObjectOwned::owned( diff --git a/types/src/params.rs b/types/src/params.rs index 6cc3b29e40..d23214dfb8 100644 --- a/types/src/params.rs +++ b/types/src/params.rs @@ -29,7 +29,6 @@ use std::fmt; -use crate::error::CallError; use anyhow::anyhow; use beef::Cow; use serde::de::{self, Deserializer, Unexpected, Visitor}; @@ -37,6 +36,9 @@ use serde::ser::Serializer; use serde::{Deserialize, Serialize}; use serde_json::Value as JsonValue; +use crate::error::{ErrorCode, INVALID_PARAMS_MSG}; +use crate::{ErrorObject, ErrorObjectOwned}; + /// JSON-RPC v2 marker type. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct TwoPointZero; @@ -117,17 +119,17 @@ impl<'a> Params<'a> { } /// Attempt to parse all parameters as an array or map into type `T`. - pub fn parse(&'a self) -> Result + pub fn parse(&'a self) -> Result where T: Deserialize<'a>, { // NOTE(niklasad1): Option::None is serialized as `null` so we provide that here. let params = self.0.as_ref().map(AsRef::as_ref).unwrap_or("null"); - serde_json::from_str(params).map_err(|e| CallError::InvalidParams(e.into())) + serde_json::from_str(params).map_err(invalid_params) } /// Attempt to parse parameters as an array of a single value of type `T`, and returns that value. - pub fn one(&'a self) -> Result + pub fn one(&'a self) -> Result where T: Deserialize<'a>, { @@ -161,7 +163,7 @@ impl<'a> Params<'a> { pub struct ParamsSequence<'a>(&'a str); impl<'a> ParamsSequence<'a> { - fn next_inner(&mut self) -> Option> + fn next_inner(&mut self) -> Option> where T: Deserialize<'a>, { @@ -178,7 +180,7 @@ impl<'a> ParamsSequence<'a> { _ => { let errmsg = format!("Invalid params. Expected one of '[', ']' or ',' but found {json:?}"); tracing::error!("[next_inner] {}", errmsg); - return Some(Err(CallError::InvalidParams(anyhow!(errmsg)))); + return Some(Err(invalid_params(errmsg))); } } @@ -199,7 +201,7 @@ impl<'a> ParamsSequence<'a> { ); self.0 = ""; - Some(Err(CallError::InvalidParams(e.into()))) + Some(Err(invalid_params(e))) } } } @@ -220,13 +222,13 @@ impl<'a> ParamsSequence<'a> { /// assert_eq!(c, "foo"); /// ``` #[allow(clippy::should_implement_trait)] - pub fn next(&mut self) -> Result + pub fn next(&mut self) -> Result where T: Deserialize<'a>, { match self.next_inner() { Some(result) => result, - None => Err(CallError::InvalidParams(anyhow!("No more params"))), + None => Err(invalid_params(anyhow!("No more params"))), } } @@ -248,7 +250,7 @@ impl<'a> ParamsSequence<'a> { /// /// assert_eq!(params, [Some(1), Some(2), None, None]); /// ``` - pub fn optional_next(&mut self) -> Result, CallError> + pub fn optional_next(&mut self) -> Result, ErrorObjectOwned> where T: Deserialize<'a>, { @@ -382,6 +384,10 @@ impl<'a> Id<'a> { } } +fn invalid_params(e: impl ToString) -> ErrorObjectOwned { + ErrorObject::owned(ErrorCode::InvalidParams.code(), INVALID_PARAMS_MSG, Some(e.to_string())) +} + #[cfg(test)] mod test { use super::{Cow, Id, JsonValue, Params, SubscriptionId, TwoPointZero};