From f28b449efa99a4d4fa7aadd937678a2ee098bcf9 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 24 Aug 2022 16:02:45 +0300 Subject: [PATCH 01/41] core: Fix doc typo Signed-off-by: Alexandru Vasile --- core/src/client/async_client/manager.rs | 8 ++++---- core/src/server/rpc_module.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/client/async_client/manager.rs b/core/src/client/async_client/manager.rs index ab1267aac3..41cc7274d2 100644 --- a/core/src/client/async_client/manager.rs +++ b/core/src/client/async_client/manager.rs @@ -181,7 +181,7 @@ impl RequestManager { } } - /// Inserts a handler for incoming notifications + /// Inserts a handler for incoming notifications. pub(crate) fn insert_notification_handler( &mut self, method: &str, @@ -195,7 +195,7 @@ impl RequestManager { } } - /// Removes a notification handler + /// Removes a notification handler. pub(crate) fn remove_notification_handler(&mut self, method: String) -> Result<(), Error> { if self.notification_handlers.remove(&method).is_some() { Ok(()) @@ -224,7 +224,7 @@ impl RequestManager { } } - /// Tries to complete a pending batch request + /// Tries to complete a pending batch request. /// /// Returns `Some` if the subscription was completed otherwise `None`. pub(crate) fn complete_pending_batch(&mut self, batch: Vec) -> Option { @@ -237,7 +237,7 @@ impl RequestManager { } } - /// Tries to complete a pending call.. + /// Tries to complete a pending call. /// /// Returns `Some` if the call was completed otherwise `None`. pub(crate) fn complete_pending_call(&mut self, request_id: RequestId) -> Option { diff --git a/core/src/server/rpc_module.rs b/core/src/server/rpc_module.rs index d19aad8295..d523a4ff25 100644 --- a/core/src/server/rpc_module.rs +++ b/core/src/server/rpc_module.rs @@ -339,7 +339,7 @@ impl Methods { /// /// The params must be serializable as JSON array, see [`ToRpcParams`] for further documentation. /// - /// Returns the decoded value of the `result field` in JSON-RPC response if succesful. + /// Returns the decoded value of the `result field` in JSON-RPC response if successful. /// /// # Examples /// From febd278ba569504f2ea2840036544bbf563872d6 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 24 Aug 2022 20:39:25 +0300 Subject: [PATCH 02/41] types: Implement generic `ParamBuilder` for RPC parameters Signed-off-by: Alexandru Vasile --- types/src/lib.rs | 1 - types/src/params.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/types/src/lib.rs b/types/src/lib.rs index 5c7b4e04bd..25cd8c3f2e 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -26,7 +26,6 @@ //! Shared types in `jsonrpsee` for clients, servers and utilities. -#![deny(unsafe_code)] #![warn(missing_docs, missing_debug_implementations)] extern crate alloc; diff --git a/types/src/params.rs b/types/src/params.rs index 5c015ef417..6afa866806 100644 --- a/types/src/params.rs +++ b/types/src/params.rs @@ -408,6 +408,65 @@ impl<'a> Id<'a> { } } +/// Initial number of bytes for a parameter length. +const PARAM_BYTES_CAPACITY: usize = 128; + +/// Generic parameter builder that serializes parameters to bytes. +/// This produces a JSON compatible String. +/// +/// The implementation relies on `Vec` to hold the serialized +/// parameters in memory for the following reasons: +/// 1. Other serialization methods than `serde_json::to_writer` would internally +/// have an extra heap allocation for temporarily holding the value in memory. +/// 2. `io::Write` is not implemented for `String` required for serialization. +#[derive(Debug)] +struct ParamsBuilder { + bytes: Vec, + end: char, +} + +impl ParamsBuilder { + /// Construct a new [`ParamsBuilder`] with custom start and end tokens. + /// The inserted values are wrapped by the _start_ and _end_ tokens. + fn new(start: char, end: char) -> Self { + let mut bytes = Vec::with_capacity(PARAM_BYTES_CAPACITY); + bytes.push(start as u8); + ParamsBuilder { bytes, end } + } + + /// Insert a named value (key, value) pair into the builder. + /// The _name_ and _value_ are delimited by the `:` token. + pub fn insert_named(&mut self, name: &str, value: P) -> Result<(), serde_json::Error> { + serde_json::to_writer(&mut self.bytes, name)?; + self.bytes.push(':' as u8); + serde_json::to_writer(&mut self.bytes, &value)?; + self.bytes.push(',' as u8); + + Ok(()) + } + + /// Insert a plain value into the builder. + pub fn insert(&mut self, value: P) -> Result<(), serde_json::Error> { + serde_json::to_writer(&mut self.bytes, &value)?; + self.bytes.push(',' as u8); + + Ok(()) + } + + /// Finish the building process and return a JSON compatible string. + pub fn build(mut self) -> String { + let idx = self.bytes.len() - 1; + if self.bytes[idx] == ',' as u8 { + self.bytes[idx] = self.end as u8; + } else { + self.bytes.push(self.end as u8); + } + + // Safety: This is safe because we do not emit invalid UTF-8. + unsafe { String::from_utf8_unchecked(self.bytes) } + } +} + #[cfg(test)] mod test { use super::{Cow, Id, JsonValue, Params, ParamsSer, SubscriptionId, TwoPointZero}; From d50de5e6cb498db171cc9b912f7a984569466033 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 24 Aug 2022 20:41:15 +0300 Subject: [PATCH 03/41] types: Add specialized RPC parameter builder for arrays and maps Signed-off-by: Alexandru Vasile --- types/src/lib.rs | 5 +- types/src/params.rs | 151 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+), 1 deletion(-) diff --git a/types/src/lib.rs b/types/src/lib.rs index 25cd8c3f2e..a34e2ed586 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -43,7 +43,10 @@ pub mod response; pub mod error; pub use error::{ErrorObject, ErrorObjectOwned, ErrorResponse, SubscriptionEmptyError, SubscriptionResult}; -pub use params::{Id, Params, ParamsSequence, ParamsSer, SubscriptionId, TwoPointZero}; +pub use params::{ + Id, NamedParams, NamedParamsBuilder, Params, ParamsSequence, ParamsSer, SubscriptionId, ToRpcParams, TwoPointZero, + UnnamedParams, UnnamedParamsBuilder, +}; pub use request::{InvalidRequest, Notification, NotificationSer, Request, RequestSer}; pub use response::{Response, SubscriptionPayload, SubscriptionResponse}; diff --git a/types/src/params.rs b/types/src/params.rs index 6afa866806..1d3ff6e061 100644 --- a/types/src/params.rs +++ b/types/src/params.rs @@ -36,6 +36,7 @@ use beef::Cow; use serde::de::{self, Deserializer, Unexpected, Visitor}; use serde::ser::Serializer; use serde::{Deserialize, Serialize}; +use serde_json::value::RawValue; use serde_json::Value as JsonValue; /// JSON-RPC v2 marker type. @@ -467,6 +468,156 @@ impl ParamsBuilder { } } +/// Parameter builder that serializes named value parameters to a JSON compatible string. +/// This is the equivalent of a JSON Map object `{ key: value }`. +/// +/// # Examples +/// +/// ```rust +/// +/// use jsonrpsee_types::NamedParamsBuilder; +/// +/// fn main() { +/// let mut builder = NamedParamsBuilder::new(); +/// builder.insert("param1", 1); +/// builder.insert("param2", "abc"); +/// let params = builder.build(); +/// +/// // Use RPC parameters... +/// } +/// ``` +#[derive(Debug)] +pub struct NamedParamsBuilder(ParamsBuilder); + +impl NamedParamsBuilder { + /// Construct a new [`NamedParamsBuilder`]. + pub fn new() -> Self { + Self(ParamsBuilder::new('{', '}')) + } + + /// Insert a named value (key, value) pair into the builder. + /// The _name_ and _value_ are delimited by the `:` token. + pub fn insert(&mut self, name: &str, value: P) -> Result<(), serde_json::Error> { + self.0.insert_named(name, value) + } + + /// Finish the building process and return a JSON compatible string. + pub fn build(self) -> NamedParams { + NamedParams(self.0.build()) + } +} + +/// Named RPC parameters stored as a JSON Map object `{ key: value }`. +#[derive(Clone, Debug)] +pub struct NamedParams(String); + +impl ToRpcParams for NamedParams { + fn to_rpc_params(self) -> Result>, serde_json::Error> { + RawValue::from_string(self.0).map(|res| Some(res)) + } +} + +/// Parameter builder that serializes plain value parameters to a JSON compatible string. +/// This is the equivalent of a JSON Array object `[ value0, value1, .., valueN ]`. +/// +/// # Examples +/// +/// ```rust +/// +/// use jsonrpsee_types::UnnamedParamsBuilder; +/// +/// fn main() { +/// let mut builder = UnnamedParamsBuilder::new(); +/// builder.insert("param1"); +/// builder.insert(1); +/// let params = builder.build(); +/// +/// // Use RPC parameters... +/// } +/// ``` +#[derive(Debug)] +pub struct UnnamedParamsBuilder(ParamsBuilder); + +impl UnnamedParamsBuilder { + /// Construct a new [`UnnamedParamsBuilder`]. + pub fn new() -> Self { + Self(ParamsBuilder::new('[', ']')) + } + + /// Insert a plain value into the builder. + pub fn insert(&mut self, value: P) -> Result<(), serde_json::Error> { + self.0.insert(value) + } + + /// Finish the building process and return a JSON compatible string. + pub fn build(self) -> UnnamedParams { + UnnamedParams(self.0.build()) + } +} + +/// Unnamed RPC parameters stored as a JSON Array object `[ value0, value1, .., valueN ]`. +#[derive(Clone, Debug)] +pub struct UnnamedParams(String); + +impl ToRpcParams for UnnamedParams { + fn to_rpc_params(self) -> Result>, serde_json::Error> { + RawValue::from_string(self.0).map(|res| Some(res)) + } +} + +/// Marker trait for types that can be serialized as JSON compatible strings. +/// +/// This trait ensures the correctness of the RPC parameters. +/// +/// # Note +/// +/// Please consider using the [`UnnamedParamsBuilder`] and [`NamedParamsBuilder`] than +/// implementing this trait. +/// +/// # Examples +/// +/// - Implementation for hard-coded strings +/// +/// ```rust +/// +/// use jsonrpsee_types::ToRpcParams; +/// use serde_json::value::RawValue; +/// +/// struct ManualParam; +/// +/// impl ToRpcParams for ManualParam { +/// fn to_rpc_params(self) -> Result>, serde_json::Error> { +/// // Manually define a valid JSONRPC parameter. +/// RawValue::from_string("[1, \"2\", 3]".to_string()).map(|res| Some(res)) +/// } +/// } +/// ``` +/// +/// - Implementation for JSON serializable structures +/// +/// ```rust +/// use jsonrpsee_types::ToRpcParams; +/// use serde_json::value::RawValue; +/// use serde::Serialize; +/// +/// #[derive(Serialize)] +/// struct SerParam { +/// param_1: u8, +/// param_2: String, +/// }; +/// +/// impl ToRpcParams for SerParam { +/// fn to_rpc_params(self) -> Result>, serde_json::Error> { +/// let s = String::from_utf8(serde_json::to_vec(&self)?).expect("Valid UTF8 format"); +/// RawValue::from_string(s).map(|res| Some(res)) +/// } +/// } +/// ``` +pub trait ToRpcParams { + /// Consume and serialize the type as a JSON raw value. + fn to_rpc_params(self) -> Result>, serde_json::Error>; +} + #[cfg(test)] mod test { use super::{Cow, Id, JsonValue, Params, ParamsSer, SubscriptionId, TwoPointZero}; From 7af14ff243d583fae093565b4559ea0ade42e5b4 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 26 Aug 2022 16:40:54 +0300 Subject: [PATCH 04/41] types: Implement parameter builder for batch requests Signed-off-by: Alexandru Vasile --- types/src/lib.rs | 4 ++-- types/src/params.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/types/src/lib.rs b/types/src/lib.rs index a34e2ed586..dbaaf90f46 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -44,8 +44,8 @@ pub mod error; pub use error::{ErrorObject, ErrorObjectOwned, ErrorResponse, SubscriptionEmptyError, SubscriptionResult}; pub use params::{ - Id, NamedParams, NamedParamsBuilder, Params, ParamsSequence, ParamsSer, SubscriptionId, ToRpcParams, TwoPointZero, - UnnamedParams, UnnamedParamsBuilder, + BatchParamsBuilder, Id, NamedParams, NamedParamsBuilder, Params, ParamsSequence, ParamsSer, SubscriptionId, + ToRpcParams, TwoPointZero, UnnamedParams, UnnamedParamsBuilder, }; pub use request::{InvalidRequest, Notification, NotificationSer, Request, RequestSer}; pub use response::{Response, SubscriptionPayload, SubscriptionResponse}; diff --git a/types/src/params.rs b/types/src/params.rs index 1d3ff6e061..9e48d5541f 100644 --- a/types/src/params.rs +++ b/types/src/params.rs @@ -618,6 +618,32 @@ pub trait ToRpcParams { fn to_rpc_params(self) -> Result>, serde_json::Error>; } +/// Initial number of parameters in a batch request. +const BATCH_PARAMS_NUM_CAPACITY: usize = 4; + +/// Parameter builder that serializes RPC parameters to construct a valid batch parameter. +/// This is the equivalent of chaining multiple RPC requests. +#[derive(Debug)] +pub struct BatchParamsBuilder<'a>(Vec<(&'a str, Option>)>); + +impl<'a> BatchParamsBuilder<'a> { + /// Construct a new [`BatchParamsBuilder`]. + pub fn new() -> Self { + Self(Vec::with_capacity(BATCH_PARAMS_NUM_CAPACITY)) + } + + /// Inserts the RPC method with provided parameters into the builder. + pub fn insert(&mut self, method: &'a str, value: Params) -> Result<(), serde_json::Error> { + self.0.push((method, value.to_rpc_params()?)); + Ok(()) + } + + /// Finish the building process and return a valid batch parameter. + pub fn build(self) -> Vec<(&'a str, Option>)> { + self.0 + } +} + #[cfg(test)] mod test { use super::{Cow, Id, JsonValue, Params, ParamsSer, SubscriptionId, TwoPointZero}; From 98c0058c80bb4a11b44b77445610a4381bde2702 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 26 Aug 2022 16:46:12 +0300 Subject: [PATCH 05/41] types: Implement `rpc_params` in the `types` crate Signed-off-by: Alexandru Vasile --- types/src/lib.rs | 7 +++++++ types/src/params.rs | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/types/src/lib.rs b/types/src/lib.rs index dbaaf90f46..42ce45754c 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -52,3 +52,10 @@ pub use response::{Response, SubscriptionPayload, SubscriptionResponse}; /// Empty `RpcParams` type; pub type EmptyParams = Vec<()>; + +#[doc(hidden)] +pub mod __reexports { + pub use crate::params::ToRpcParams; + pub use crate::params::UnnamedParams; + pub use crate::params::UnnamedParamsBuilder; +} diff --git a/types/src/params.rs b/types/src/params.rs index 9e48d5541f..cc38d62064 100644 --- a/types/src/params.rs +++ b/types/src/params.rs @@ -644,6 +644,31 @@ impl<'a> BatchParamsBuilder<'a> { } } +/// Custom implementation to provide a type which contains to RPC parameters. +impl ToRpcParams for () { + fn to_rpc_params(self) -> Result>, serde_json::Error> { + Ok(None) + } +} + +#[macro_export] +/// Convert the given values to a [`jsonrpsee_types::UnnamedParams`] as expected by a jsonrpsee Client (http or websocket). +macro_rules! rpc_params { + () => { + // ToRpcParams is implemented for the empty tuple. + () + }; + ($($param:expr),*) => { + { + let mut __params = $crate::__reexports::UnnamedParamsBuilder::new(); + $( + __params.insert($param).expect("json serialization is infallible; qed."); + )* + __params.build() + } + }; +} + #[cfg(test)] mod test { use super::{Cow, Id, JsonValue, Params, ParamsSer, SubscriptionId, TwoPointZero}; From bf7cd86f82d816325d4db32932a040e54abeb42e Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 26 Aug 2022 18:06:47 +0300 Subject: [PATCH 06/41] core: Adjust `ClientT` for generic efficient parameters Signed-off-by: Alexandru Vasile --- client/http-client/src/client.rs | 22 ++++++++---- core/src/client/async_client/helpers.rs | 14 ++++---- core/src/client/async_client/mod.rs | 48 +++++++++++++++---------- core/src/client/mod.rs | 31 +++++++++------- types/src/lib.rs | 2 +- types/src/request.rs | 48 +++++++++++++++++-------- 6 files changed, 105 insertions(+), 60 deletions(-) diff --git a/client/http-client/src/client.rs b/client/http-client/src/client.rs index 541459c03e..078f2cc4bf 100644 --- a/client/http-client/src/client.rs +++ b/client/http-client/src/client.rs @@ -28,15 +28,17 @@ use std::sync::Arc; use std::time::Duration; use crate::transport::HttpTransportClient; -use crate::types::{ErrorResponse, Id, NotificationSer, ParamsSer, RequestSer, Response}; +use crate::types::{ErrorResponse, Id, NotificationSer, RequestSer, Response}; use async_trait::async_trait; use hyper::http::HeaderMap; use jsonrpsee_core::client::{CertificateStore, ClientT, IdKind, RequestIdManager, Subscription, SubscriptionClientT}; use jsonrpsee_core::tracing::RpcTracing; use jsonrpsee_core::{Error, TEN_MB_SIZE_BYTES}; use jsonrpsee_types::error::CallError; +use jsonrpsee_types::params::ToRpcParams; use rustc_hash::FxHashMap; use serde::de::DeserializeOwned; +use serde_json::value::RawValue; use tracing_futures::Instrument; /// Http Client Builder. @@ -166,9 +168,13 @@ pub struct HttpClient { #[async_trait] impl ClientT for HttpClient { - async fn notification<'a>(&self, method: &'a str, params: Option>) -> Result<(), Error> { + async fn notification(&self, method: &str, params: Params) -> Result<(), Error> + where + Params: ToRpcParams + Send, + { let trace = RpcTracing::notification(method); async { + let params = params.to_rpc_params()?; let notif = serde_json::to_string(&NotificationSer::new(method, params)).map_err(Error::ParseError)?; let fut = self.transport.send(notif); @@ -184,12 +190,15 @@ impl ClientT for HttpClient { } /// Perform a request towards the server. - async fn request<'a, R>(&self, method: &'a str, params: Option>) -> Result + async fn request(&self, method: &str, params: Params) -> Result where R: DeserializeOwned, + Params: ToRpcParams + Send, { let guard = self.id_manager.next_request_id()?; let id = guard.inner(); + let params = params.to_rpc_params()?; + let request = RequestSer::new(&id, method, params); let trace = RpcTracing::method_call(method); @@ -225,7 +234,7 @@ impl ClientT for HttpClient { .await } - async fn batch_request<'a, R>(&self, batch: Vec<(&'a str, Option>)>) -> Result, Error> + async fn batch_request(&self, batch: Vec<(&str, Option>)>) -> Result, Error> where R: DeserializeOwned + Default + Clone, { @@ -279,13 +288,14 @@ impl ClientT for HttpClient { #[async_trait] impl SubscriptionClientT for HttpClient { /// Send a subscription request to the server. Not implemented for HTTP; will always return [`Error::HttpNotImplemented`]. - async fn subscribe<'a, N>( + async fn subscribe<'a, N, Params>( &self, _subscribe_method: &'a str, - _params: Option>, + _params: Params, _unsubscribe_method: &'a str, ) -> Result, Error> where + Params: ToRpcParams + Send, N: DeserializeOwned, { Err(Error::HttpNotImplemented) diff --git a/core/src/client/async_client/helpers.rs b/core/src/client/async_client/helpers.rs index bda9ef83fb..a0bfd7fab2 100644 --- a/core/src/client/async_client/helpers.rs +++ b/core/src/client/async_client/helpers.rs @@ -34,9 +34,7 @@ use futures_util::future::{self, Either}; use jsonrpsee_types::error::CallError; use jsonrpsee_types::response::SubscriptionError; -use jsonrpsee_types::{ - ErrorResponse, Id, Notification, ParamsSer, RequestSer, Response, SubscriptionId, SubscriptionResponse, -}; +use jsonrpsee_types::{ErrorResponse, Id, Notification, RequestSer, Response, SubscriptionId, SubscriptionResponse, ToRpcParams, UnnamedParamsBuilder}; use serde_json::Value as JsonValue; /// Attempts to process a batch response. @@ -222,10 +220,12 @@ pub(crate) fn build_unsubscribe_message( sub_id: SubscriptionId<'static>, ) -> Option { let (unsub_req_id, _, unsub, sub_id) = manager.remove_subscription(sub_req_id, sub_id)?; - let sub_id_slice: &[JsonValue] = &[sub_id.into()]; - // TODO: https://github.com/paritytech/jsonrpsee/issues/275 - let params = ParamsSer::ArrayRef(sub_id_slice); - let raw = serde_json::to_string(&RequestSer::new(&unsub_req_id, &unsub, Some(params))).ok()?; + + let mut params = UnnamedParamsBuilder::new(); + params.insert(sub_id).ok()?; + let params = params.build().to_rpc_params().ok()?; + + let raw = serde_json::to_string(&RequestSer::new(&unsub_req_id, &unsub, params)).ok()?; Some(RequestMessage { raw, id: unsub_req_id, send_back: None }) } diff --git a/core/src/client/async_client/mod.rs b/core/src/client/async_client/mod.rs index 201a74c1e7..8944204fda 100644 --- a/core/src/client/async_client/mod.rs +++ b/core/src/client/async_client/mod.rs @@ -27,11 +27,13 @@ use futures_util::sink::SinkExt; use futures_util::stream::StreamExt; use futures_util::FutureExt; use jsonrpsee_types::{ - response::SubscriptionError, ErrorResponse, Id, Notification, NotificationSer, ParamsSer, RequestSer, Response, + response::SubscriptionError, ErrorResponse, Id, Notification, NotificationSer, RequestSer, Response, SubscriptionResponse, }; use serde::de::DeserializeOwned; +use serde_json::value::RawValue; use tracing_futures::Instrument; +use jsonrpsee_types::params::ToRpcParams; use super::{FrontToBack, IdKind, RequestIdManager}; @@ -174,7 +176,7 @@ impl ClientBuilder { ping_interval, on_close_tx, ) - .await; + .await; }); Client { to_back, @@ -272,10 +274,14 @@ impl Drop for Client { } #[async_trait] -impl ClientT for Client { - async fn notification<'a>(&self, method: &'a str, params: Option>) -> Result<(), Error> { +impl ClientT for Client +{ + async fn notification(&self, method: &str, params: Params) -> Result<(), Error> + where + Params: ToRpcParams + Send { // NOTE: we use this to guard against max number of concurrent requests. let _req_id = self.id_manager.next_request_id()?; + let params = params.to_rpc_params()?; let notif = NotificationSer::new(method, params); let trace = RpcTracing::batch(); @@ -292,13 +298,14 @@ impl ClientT for Client { Either::Right((_, _)) => Err(Error::RequestTimeout), } } - .instrument(trace.into_span()) - .await + .instrument(trace.into_span()) + .await } - async fn request<'a, R>(&self, method: &'a str, params: Option>) -> Result + async fn request(&self, method: &str, params: Params) -> Result where R: DeserializeOwned, + Params: ToRpcParams + Send { let (send_back_tx, send_back_rx) = oneshot::channel(); let guard = self.id_manager.next_request_id()?; @@ -306,6 +313,7 @@ impl ClientT for Client { let trace = RpcTracing::method_call(method); async { + let params = params.to_rpc_params()?; let raw = serde_json::to_string(&RequestSer::new(&id, method, params)).map_err(Error::ParseError)?; tx_log_from_str(&raw, self.max_log_length); @@ -330,13 +338,13 @@ impl ClientT for Client { serde_json::from_value(json_value).map_err(Error::ParseError) } - .instrument(trace.into_span()) - .await + .instrument(trace.into_span()) + .await } - async fn batch_request<'a, R>(&self, batch: Vec<(&'a str, Option>)>) -> Result, Error> + async fn batch_request(&self, batch: Vec<(&str, Option>)>) -> Result, Error> where - R: DeserializeOwned + Default + Clone, + R: DeserializeOwned + Default + Clone { let trace = RpcTracing::batch(); async { @@ -374,8 +382,8 @@ impl ClientT for Client { json_values.into_iter().map(|val| serde_json::from_value(val).map_err(Error::ParseError)).collect() } - .instrument(trace.into_span()) - .await + .instrument(trace.into_span()) + .await } } @@ -385,14 +393,15 @@ impl SubscriptionClientT for Client { /// /// The `subscribe_method` and `params` are used to ask for the subscription towards the /// server. The `unsubscribe_method` is used to close the subscription. - async fn subscribe<'a, N>( + async fn subscribe<'a, Notif, Params>( &self, subscribe_method: &'a str, - params: Option>, + params: Params, unsubscribe_method: &'a str, - ) -> Result, Error> + ) -> Result, Error> where - N: DeserializeOwned, + Params: ToRpcParams + Send, + Notif: DeserializeOwned { if subscribe_method == unsubscribe_method { return Err(Error::SubscriptionNameConflict(unsubscribe_method.to_owned())); @@ -401,6 +410,7 @@ impl SubscriptionClientT for Client { let guard = self.id_manager.next_request_ids(2)?; let mut ids: Vec = guard.inner(); let trace = RpcTracing::method_call(subscribe_method); + let params = params.to_rpc_params()?; async { let id = ids[0].clone(); @@ -439,8 +449,8 @@ impl SubscriptionClientT for Client { Ok(Subscription::new(self.to_back.clone(), notifs_rx, SubscriptionKind::Subscription(sub_id))) } - .instrument(trace.into_span()) - .await + .instrument(trace.into_span()) + .await } /// Subscribe to a specific method. diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index 44566b55e2..67833978bf 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -38,14 +38,16 @@ use futures_channel::{mpsc, oneshot}; use futures_util::future::FutureExt; use futures_util::sink::SinkExt; use futures_util::stream::{Stream, StreamExt}; -use jsonrpsee_types::{Id, ParamsSer, SubscriptionId}; +use jsonrpsee_types::{Id, SubscriptionId}; use serde::de::DeserializeOwned; use serde_json::Value as JsonValue; +use serde_json::value::RawValue; +use jsonrpsee_types::params::ToRpcParams; #[doc(hidden)] pub mod __reexports { pub use crate::to_json_value; - pub use jsonrpsee_types::ParamsSer; + pub use jsonrpsee_types::UnnamedParamsBuilder; } cfg_async_client! { @@ -57,12 +59,15 @@ cfg_async_client! { #[async_trait] pub trait ClientT { /// Send a [notification request](https://www.jsonrpc.org/specification#notification) - async fn notification<'a>(&self, method: &'a str, params: Option>) -> Result<(), Error>; + async fn notification(&self, method: &str, params: Params) -> Result<(), Error> + where + Params: ToRpcParams + Send; /// Send a [method call request](https://www.jsonrpc.org/specification#request_object). - async fn request<'a, R>(&self, method: &'a str, params: Option>) -> Result + async fn request(&self, method: &str, params: Params) -> Result where - R: DeserializeOwned; + R: DeserializeOwned, + Params: ToRpcParams + Send; /// Send a [batch request](https://www.jsonrpc.org/specification#batch). /// @@ -70,7 +75,7 @@ pub trait ClientT { /// /// Returns `Ok` if all requests in the batch were answered successfully. /// Returns `Error` if any of the requests in batch fails. - async fn batch_request<'a, R>(&self, batch: Vec<(&'a str, Option>)>) -> Result, Error> + async fn batch_request(&self, batch: Vec<(&str, Option>)>) -> Result, Error> where R: DeserializeOwned + Default + Clone; } @@ -90,13 +95,14 @@ pub trait SubscriptionClientT: ClientT { /// /// The `Notif` param is a generic type to receive generic subscriptions, see [`Subscription`] for further /// documentation. - async fn subscribe<'a, Notif>( + async fn subscribe<'a, Notif, Params>( &self, subscribe_method: &'a str, - params: Option>, + params: Params, unsubscribe_method: &'a str, ) -> Result, Error> where + Params: ToRpcParams + Send, Notif: DeserializeOwned; /// Register a method subscription, this is used to filter only server notifications that a user is interested in. @@ -177,15 +183,16 @@ pub trait TransportReceiverT: 'static { macro_rules! rpc_params { ($($param:expr),*) => { { - let mut __params = vec![]; + let mut __params = UnnamedParamsBuilder::new(); $( - __params.push($crate::client::__reexports::to_json_value($param).expect("json serialization is infallible; qed.")); + __params.insert($param).expect("json serialization is infallible; qed."); )* - Some($crate::client::__reexports::ParamsSer::Array(__params)) + __params.build() } }; () => { - None + // ToRpcParams is implemented for the empty tuple. + () } } diff --git a/types/src/lib.rs b/types/src/lib.rs index 42ce45754c..60874e64f5 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -50,7 +50,7 @@ pub use params::{ pub use request::{InvalidRequest, Notification, NotificationSer, Request, RequestSer}; pub use response::{Response, SubscriptionPayload, SubscriptionResponse}; -/// Empty `RpcParams` type; +/// Empty `RpcParams` type. pub type EmptyParams = Vec<()>; #[doc(hidden)] diff --git a/types/src/request.rs b/types/src/request.rs index 1cbd1ee2d6..a197c0e9e8 100644 --- a/types/src/request.rs +++ b/types/src/request.rs @@ -27,7 +27,7 @@ //! Types to handle JSON-RPC requests according to the [spec](https://www.jsonrpc.org/specification#request-object). //! Some types come with a "*Ser" variant that implements [`serde::Serialize`]; these are used in the client. -use crate::params::{Id, ParamsSer, TwoPointZero}; +use crate::params::{Id, TwoPointZero}; use beef::Cow; use serde::{Deserialize, Serialize}; use serde_json::value::RawValue; @@ -96,12 +96,12 @@ pub struct RequestSer<'a> { pub method: &'a str, /// Parameter values of the request. #[serde(skip_serializing_if = "Option::is_none")] - pub params: Option>, + pub params: Option>, } impl<'a> RequestSer<'a> { /// Create a new serializable JSON-RPC request. - pub fn new(id: &'a Id<'a>, method: &'a str, params: Option>) -> Self { + pub fn new(id: &'a Id<'a>, method: &'a str, params: Option>) -> Self { Self { jsonrpc: TwoPointZero, id, method, params } } } @@ -117,20 +117,21 @@ pub struct NotificationSer<'a> { pub method: &'a str, /// Parameter values of the request. #[serde(skip_serializing_if = "Option::is_none")] - pub params: Option>, + pub params: Option>, } impl<'a> NotificationSer<'a> { /// Create a new serializable JSON-RPC request. - pub fn new(method: &'a str, params: Option>) -> Self { + pub fn new(method: &'a str, params: Option>) -> Self { Self { jsonrpc: TwoPointZero, method, params } } } #[cfg(test)] mod test { - use super::{Id, InvalidRequest, Notification, NotificationSer, ParamsSer, Request, RequestSer, TwoPointZero}; - use serde_json::{value::RawValue, Value}; + use super::{Id, InvalidRequest, Notification, NotificationSer, Request, RequestSer, TwoPointZero}; + use crate::{rpc_params, ToRpcParams}; + use serde_json::value::RawValue; fn assert_request<'a>(request: Request<'a>, id: Id<'a>, method: &str, params: Option<&str>) { assert_eq!(request.jsonrpc, TwoPointZero); @@ -206,23 +207,39 @@ mod test { fn serialize_call() { let method = "subtract"; let id = Id::Number(1); // It's enough to check one variant, since the type itself also has tests. - let params: ParamsSer = vec![Value::Number(42.into()), Value::Number(23.into())].into(); // Same as above. - let test_vector = &[ + let params = rpc_params![42, 23]; // Same as above. + + let test_vector: &[(&'static str, Option<_>, Option<_>, &'static str)] = &[ // With all fields set. ( r#"{"jsonrpc":"2.0","id":1,"method":"subtract","params":[42,23]}"#, Some(&id), - Some(params.clone()), + params.clone().to_rpc_params().unwrap(), method, ), // Escaped method name. - (r#"{"jsonrpc":"2.0","id":1,"method":"\"m"}"#, Some(&id), None, "\"m"), + (r#"{"jsonrpc":"2.0","id":1,"method":"\"m"}"#, Some(&id), rpc_params![].to_rpc_params().unwrap(), "\"m"), // Without ID field. - (r#"{"jsonrpc":"2.0","id":null,"method":"subtract","params":[42,23]}"#, None, Some(params), method), + ( + r#"{"jsonrpc":"2.0","id":null,"method":"subtract","params":[42,23]}"#, + None, + params.to_rpc_params().unwrap(), + method, + ), // Without params field - (r#"{"jsonrpc":"2.0","id":1,"method":"subtract"}"#, Some(&id), None, method), + ( + r#"{"jsonrpc":"2.0","id":1,"method":"subtract"}"#, + Some(&id), + rpc_params![].to_rpc_params().unwrap(), + method, + ), // Without params and ID. - (r#"{"jsonrpc":"2.0","id":null,"method":"subtract"}"#, None, None, method), + ( + r#"{"jsonrpc":"2.0","id":null,"method":"subtract"}"#, + None, + rpc_params![].to_rpc_params().unwrap(), + method, + ), ]; for (ser, id, params, method) in test_vector.iter().cloned() { @@ -241,7 +258,8 @@ mod test { #[test] fn serialize_notif() { let exp = r#"{"jsonrpc":"2.0","method":"say_hello","params":["hello"]}"#; - let req = NotificationSer::new("say_hello", Some(vec!["hello".into()].into())); + let params = rpc_params!["hello"].to_rpc_params().unwrap(); + let req = NotificationSer::new("say_hello", params); let ser = serde_json::to_string(&req).unwrap(); assert_eq!(exp, ser); } From 32c5f513579387ad595967eef10d087efce0f13d Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 26 Aug 2022 18:23:40 +0300 Subject: [PATCH 07/41] proc-macro: Render clients using the parameter builders Signed-off-by: Alexandru Vasile --- proc-macros/src/render_client.rs | 70 ++++++++++++++++---------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/proc-macros/src/render_client.rs b/proc-macros/src/render_client.rs index 64348242ef..2fb36badb2 100644 --- a/proc-macros/src/render_client.rs +++ b/proc-macros/src/render_client.rs @@ -54,7 +54,6 @@ impl RpcDescription { // Doc-comment to be associated with the client. let doc_comment = format!("Client implementation for the `{}` RPC API.", &self.trait_def.ident); - let trait_impl = quote! { #[#async_trait] #[doc = #doc_comment] @@ -95,7 +94,7 @@ impl RpcDescription { }; // Encoded parameters for the request. - let parameters = self.encode_params(&method.params, &method.param_kind, &method.signature); + let parameter_builder = self.encode_params(&method.params, &method.param_kind, &method.signature); // Doc-comment to be associated with the method. let docs = &method.docs; // Mark the method as deprecated, if previously declared as so. @@ -105,7 +104,8 @@ impl RpcDescription { #docs #deprecated async fn #rust_method_name(#rust_method_params) -> #returns { - self.#called_method(#rpc_method_name, #parameters).await + let params = { #parameter_builder }; + self.#called_method(#rpc_method_name, params).await } }; Ok(method) @@ -130,14 +130,15 @@ impl RpcDescription { let returns = quote! { Result<#sub_type<#item>, #jrps_error> }; // Encoded parameters for the request. - let parameters = self.encode_params(&sub.params, &sub.param_kind, &sub.signature); + let parameter_builder = self.encode_params(&sub.params, &sub.param_kind, &sub.signature); // Doc-comment to be associated with the method. let docs = &sub.docs; let method = quote! { #docs async fn #rust_method_name(#rust_method_params) -> #returns { - self.subscribe(#rpc_sub_name, #parameters, #rpc_unsub_name).await + let params = { #parameter_builder }; + self.subscribe(#rpc_sub_name, params, #rpc_unsub_name).await } }; Ok(method) @@ -149,39 +150,38 @@ impl RpcDescription { param_kind: &ParamKind, signature: &syn::TraitItemMethod, ) -> TokenStream2 { - if !params.is_empty() { - let serde_json = self.jrps_client_item(quote! { core::__reexports::serde_json }); - let params = params.iter().map(|(param, _param_type)| { - quote! { #serde_json::to_value(&#param)? } - }); - match param_kind { - ParamKind::Map => { - let jsonrpsee = self.jsonrpsee_client_path.as_ref().unwrap(); - // Extract parameter names. - let param_names = extract_param_names(&signature.sig); - // Combine parameter names and values into tuples. - let params = param_names.iter().zip(params).map(|pair| { - let param = pair.0; - let value = pair.1; - quote! { (#param, #value) } - }); - quote! { - Some(#jsonrpsee::types::ParamsSer::Map( - std::collections::BTreeMap::<&str, #serde_json::Value>::from( - [#(#params),*] - ) - ) - ) - } + if params.is_empty() { + return quote! { () }; + } + + let jsonrpsee = self.jsonrpsee_client_path.as_ref().unwrap(); + + match param_kind { + ParamKind::Map => { + // Extract parameter names. + let param_names = extract_param_names(&signature.sig); + // Combine parameter names and values to pass them as parameters. + let params_insert = param_names.iter().zip(params).map(|pair| { + let name = pair.0; + // Throw away the type. + let (value, _value_type) = pair.1; + quote! { #name, #value } + }); + quote! { + let mut builder = #jsonrpsee::types::NamedParamsBuilder::new(); + #( builder.insert( #params_insert ).expect(format!("Parameters {} must be valid", stringify!(#params_insert)).as_str()); )* + builder.build() } - ParamKind::Array => { - quote! { - Some(vec![ #(#params),* ].into()) - } + } + ParamKind::Array => { + // Throw away the type. + let params = params.iter().map(|(param, _param_type)| param); + quote! { + let mut builder = #jsonrpsee::types::UnnamedParamsBuilder::new(); + #( builder.insert( #params ).expect(format!("Parameters {} must be valid", stringify!(#params)).as_str()); )* + builder.build() } } - } else { - quote! { None } } } } From 1bc535f2bd39ae4df3129f4bed6beb5b23301453 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 26 Aug 2022 15:54:20 +0300 Subject: [PATCH 08/41] Adjust testing to the `ToRpcParams` interface Signed-off-by: Alexandru Vasile --- client/http-client/src/tests.rs | 35 +++--- client/ws-client/src/tests.rs | 38 +++--- core/src/client/async_client/mod.rs | 12 +- examples/examples/core_client.rs | 3 +- examples/examples/http.rs | 2 +- examples/examples/http_middleware.rs | 7 +- examples/examples/http_proxy_middleware.rs | 3 +- examples/examples/logger_http.rs | 7 +- examples/examples/logger_ws.rs | 7 +- examples/examples/multi_logger.rs | 8 +- examples/examples/proc_macro.rs | 106 ++-------------- examples/examples/ws.rs | 3 +- examples/examples/ws_pubsub_with_params.rs | 10 +- jsonrpsee/src/lib.rs | 2 +- proc-macros/tests/ui/correct/basic.rs | 9 +- .../ui/incorrect/rpc/rpc_empty_bounds.stderr | 2 +- tests/tests/integration_tests.rs | 117 ++++++++++-------- tests/tests/metrics.rs | 32 +++-- tests/tests/proc_macros.rs | 34 ++--- tests/tests/resource_limiting.rs | 39 +++--- types/src/params.rs | 7 ++ 21 files changed, 229 insertions(+), 254 deletions(-) diff --git a/client/http-client/src/tests.rs b/client/http-client/src/tests.rs index 17435828fc..cc79c4ad9e 100644 --- a/client/http-client/src/tests.rs +++ b/client/http-client/src/tests.rs @@ -25,15 +25,16 @@ // DEALINGS IN THE SOFTWARE. use crate::types::error::{ErrorCode, ErrorObject}; -use crate::types::ParamsSer; + use crate::HttpClientBuilder; use jsonrpsee_core::client::{ClientT, IdKind}; -use jsonrpsee_core::rpc_params; +use serde_json::value::RawValue; use jsonrpsee_core::Error; 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::{rpc_params, BatchParamsBuilder}; #[tokio::test] async fn method_call_works() { @@ -52,10 +53,8 @@ async fn method_call_with_wrong_id_kind() { http_server_with_hardcoded_response(ok_response(exp.into(), Id::Num(0))).with_default_timeout().await.unwrap(); let uri = format!("http://{}", server_addr); let client = HttpClientBuilder::default().id_format(IdKind::String).build(&uri).unwrap(); - assert!(matches!( - client.request::("o", None).with_default_timeout().await.unwrap(), - Err(Error::InvalidRequestId) - )); + let res: Result = client.request("o", rpc_params![]).with_default_timeout().await.unwrap(); + assert!(matches!(res, Err(Error::InvalidRequestId))); } #[tokio::test] @@ -67,7 +66,7 @@ async fn method_call_with_id_str() { .unwrap(); let uri = format!("http://{}", server_addr); let client = HttpClientBuilder::default().id_format(IdKind::String).build(&uri).unwrap(); - let response: String = client.request::("o", None).with_default_timeout().await.unwrap().unwrap(); + let response: String = client.request("o", rpc_params![]).with_default_timeout().await.unwrap().unwrap(); assert_eq!(&response, exp); } @@ -77,7 +76,7 @@ async fn notification_works() { let uri = format!("http://{}", server_addr); let client = HttpClientBuilder::default().build(&uri).unwrap(); client - .notification("i_dont_care_about_the_response_because_the_server_should_not_respond", None) + .notification("i_dont_care_about_the_response_because_the_server_should_not_respond", rpc_params![]) .with_default_timeout() .await .unwrap() @@ -137,8 +136,11 @@ async fn subscription_response_to_request() { #[tokio::test] async fn batch_request_works() { - let batch_request = - vec![("say_hello", rpc_params![]), ("say_goodbye", rpc_params![0_u64, 1, 2]), ("get_swag", None)]; + let mut builder = BatchParamsBuilder::new(); + builder.insert("say_hello", rpc_params![]).unwrap(); + builder.insert("say_goodbye", rpc_params![0_u64, 1, 2]).unwrap(); + builder.insert("get_swag", rpc_params![]).unwrap(); + let batch_request = builder.build(); let server_response = r#"[{"jsonrpc":"2.0","result":"hello","id":0}, {"jsonrpc":"2.0","result":"goodbye","id":1}, {"jsonrpc":"2.0","result":"here's your swag","id":2}]"#.to_string(); let response = run_batch_request_with_response(batch_request, server_response).with_default_timeout().await.unwrap().unwrap(); @@ -147,16 +149,19 @@ async fn batch_request_works() { #[tokio::test] async fn batch_request_out_of_order_response() { - let batch_request = - vec![("say_hello", rpc_params! {}), ("say_goodbye", rpc_params![0_u64, 1, 2]), ("get_swag", None)]; + let mut builder = BatchParamsBuilder::new(); + builder.insert("say_hello", rpc_params![]).unwrap(); + builder.insert("say_goodbye", rpc_params![0_u64, 1, 2]).unwrap(); + builder.insert("get_swag", rpc_params![]).unwrap(); + let batch_request = builder.build(); let server_response = r#"[{"jsonrpc":"2.0","result":"here's your swag","id":2}, {"jsonrpc":"2.0","result":"hello","id":0}, {"jsonrpc":"2.0","result":"goodbye","id":1}]"#.to_string(); let response = run_batch_request_with_response(batch_request, server_response).with_default_timeout().await.unwrap().unwrap(); assert_eq!(response, vec!["hello".to_string(), "goodbye".to_string(), "here's your swag".to_string()]); } -async fn run_batch_request_with_response<'a>( - batch: Vec<(&'a str, Option>)>, +async fn run_batch_request_with_response( + batch: Vec<(&str, Option>)>, response: String, ) -> Result, Error> { let server_addr = http_server_with_hardcoded_response(response).with_default_timeout().await.unwrap(); @@ -169,7 +174,7 @@ async fn run_request_with_response(response: String) -> Result { let server_addr = http_server_with_hardcoded_response(response).with_default_timeout().await.unwrap(); let uri = format!("http://{}", server_addr); let client = HttpClientBuilder::default().build(&uri).unwrap(); - client.request("say_hello", None).with_default_timeout().await.unwrap() + client.request("say_hello", rpc_params![]).with_default_timeout().await.unwrap() } fn assert_jsonrpc_error_response(err: Error, exp: ErrorObjectOwned) { diff --git a/client/ws-client/src/tests.rs b/client/ws-client/src/tests.rs index 11ec298c08..e45aca8ead 100644 --- a/client/ws-client/src/tests.rs +++ b/client/ws-client/src/tests.rs @@ -26,16 +26,17 @@ #![cfg(test)] use crate::types::error::{ErrorCode, ErrorObject}; -use crate::types::ParamsSer; + use crate::WsClientBuilder; use jsonrpsee_core::client::{ClientT, SubscriptionClientT}; use jsonrpsee_core::client::{IdKind, Subscription}; -use jsonrpsee_core::rpc_params; use jsonrpsee_core::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::{rpc_params, BatchParamsBuilder}; +use serde_json::value::RawValue; use serde_json::Value as JsonValue; #[tokio::test] @@ -62,7 +63,7 @@ async fn method_call_with_wrong_id_kind() { let client = WsClientBuilder::default().id_format(IdKind::String).build(&uri).with_default_timeout().await.unwrap().unwrap(); - let err = client.request::("o", None).with_default_timeout().await.unwrap(); + let err: Result = client.request("o", rpc_params![]).with_default_timeout().await.unwrap(); assert!(matches!(err, Err(Error::RestartNeeded(e)) if e == "Invalid request ID")); } @@ -79,7 +80,7 @@ async fn method_call_with_id_str() { let uri = format!("ws://{}", server.local_addr()); let client = WsClientBuilder::default().id_format(IdKind::String).build(&uri).with_default_timeout().await.unwrap().unwrap(); - let response: String = client.request::("o", None).with_default_timeout().await.unwrap().unwrap(); + let response: String = client.request("o", rpc_params![]).with_default_timeout().await.unwrap().unwrap(); assert_eq!(&response, exp); } @@ -92,7 +93,7 @@ async fn notif_works() { .unwrap(); let uri = to_ws_uri_string(server.local_addr()); let client = WsClientBuilder::default().build(&uri).with_default_timeout().await.unwrap().unwrap(); - assert!(client.notification("notif", None).with_default_timeout().await.unwrap().is_ok()); + assert!(client.notification("notif", rpc_params![]).with_default_timeout().await.unwrap().is_ok()); } #[tokio::test] @@ -153,7 +154,7 @@ async fn subscription_works() { let client = WsClientBuilder::default().build(&uri).with_default_timeout().await.unwrap().unwrap(); { let mut sub: Subscription = client - .subscribe("subscribe_hello", None, "unsubscribe_hello") + .subscribe("subscribe_hello", rpc_params![], "unsubscribe_hello") .with_default_timeout() .await .unwrap() @@ -226,7 +227,11 @@ async fn notification_without_polling_doesnt_make_client_unuseable() { #[tokio::test] async fn batch_request_works() { - let batch_request = vec![("say_hello", None), ("say_goodbye", rpc_params![0_u64, 1, 2]), ("get_swag", None)]; + let mut builder = BatchParamsBuilder::new(); + builder.insert("say_hello", rpc_params![]).unwrap(); + builder.insert("say_goodbye", rpc_params![0_u64, 1, 2]).unwrap(); + builder.insert("get_swag", rpc_params![]).unwrap(); + let batch_request = builder.build(); let server_response = r#"[{"jsonrpc":"2.0","result":"hello","id":0}, {"jsonrpc":"2.0","result":"goodbye","id":1}, {"jsonrpc":"2.0","result":"here's your swag","id":2}]"#.to_string(); let response = run_batch_request_with_response(batch_request, server_response).with_default_timeout().await.unwrap().unwrap(); @@ -235,7 +240,11 @@ async fn batch_request_works() { #[tokio::test] async fn batch_request_out_of_order_response() { - let batch_request = vec![("say_hello", None), ("say_goodbye", rpc_params![0_u64, 1, 2]), ("get_swag", None)]; + let mut builder = BatchParamsBuilder::new(); + builder.insert("say_hello", rpc_params![]).unwrap(); + builder.insert("say_goodbye", rpc_params![0_u64, 1, 2]).unwrap(); + builder.insert("get_swag", rpc_params![]).unwrap(); + let batch_request = builder.build(); let server_response = r#"[{"jsonrpc":"2.0","result":"here's your swag","id":2}, {"jsonrpc":"2.0","result":"hello","id":0}, {"jsonrpc":"2.0","result":"goodbye","id":1}]"#.to_string(); let response = run_batch_request_with_response(batch_request, server_response).with_default_timeout().await.unwrap().unwrap(); @@ -260,15 +269,16 @@ async fn is_connected_works() { let client = WsClientBuilder::default().build(&uri).with_default_timeout().await.unwrap().unwrap(); assert!(client.is_connected()); - client.request::("say_hello", None).with_default_timeout().await.unwrap().unwrap_err(); + let res: Result = client.request("say_hello", rpc_params![]).with_default_timeout().await.unwrap(); + res.unwrap_err(); // give the background thread some time to terminate. tokio::time::sleep(std::time::Duration::from_millis(100)).await; assert!(!client.is_connected()) } -async fn run_batch_request_with_response<'a>( - batch: Vec<(&'a str, Option>)>, +async fn run_batch_request_with_response( + batch: Vec<(&str, Option>)>, response: String, ) -> Result, Error> { let server = WebSocketTestServer::with_hardcoded_response("127.0.0.1:0".parse().unwrap(), response) @@ -287,7 +297,7 @@ async fn run_request_with_response(response: String) -> Result { .unwrap(); let uri = format!("ws://{}", server.local_addr()); let client = WsClientBuilder::default().build(&uri).with_default_timeout().await.unwrap().unwrap(); - client.request("say_hello", None).with_default_timeout().await.unwrap() + client.request("say_hello", rpc_params![]).with_default_timeout().await.unwrap() } fn assert_error_response(err: Error, exp: ErrorObjectOwned) { @@ -326,6 +336,6 @@ async fn redirections() { // It's connected assert!(client.is_connected()); // It works - let response = client.request::("anything", None).with_default_timeout().await.unwrap(); - assert_eq!(response.unwrap(), String::from(expected)); + let response: String = client.request("anything", rpc_params![]).with_default_timeout().await.unwrap().unwrap(); + assert_eq!(response, String::from(expected)); } diff --git a/core/src/client/async_client/mod.rs b/core/src/client/async_client/mod.rs index 8944204fda..f96ad4eee7 100644 --- a/core/src/client/async_client/mod.rs +++ b/core/src/client/async_client/mod.rs @@ -156,9 +156,9 @@ impl ClientBuilder { #[cfg(feature = "async-client")] #[cfg_attr(docsrs, doc(cfg(feature = "async-client")))] pub fn build_with_tokio(self, sender: S, receiver: R) -> Client - where - S: TransportSenderT + Send, - R: TransportReceiverT + Send, + where + S: TransportSenderT + Send, + R: TransportReceiverT + Send, { let (to_back, from_front) = mpsc::channel(self.max_concurrent_requests); let (err_tx, err_rx) = oneshot::channel(); @@ -192,9 +192,9 @@ impl ClientBuilder { #[cfg(all(feature = "async-wasm-client", target_arch = "wasm32"))] #[cfg_attr(docsrs, doc(cfg(feature = "async-wasm-client")))] pub fn build_with_wasm(self, sender: S, receiver: R) -> Client - where - S: TransportSenderT, - R: TransportReceiverT, + where + S: TransportSenderT, + R: TransportReceiverT, { let (to_back, from_front) = mpsc::channel(self.max_concurrent_requests); let (err_tx, err_rx) = oneshot::channel(); diff --git a/examples/examples/core_client.rs b/examples/examples/core_client.rs index 820c2c12c5..aa741e935f 100644 --- a/examples/examples/core_client.rs +++ b/examples/examples/core_client.rs @@ -28,6 +28,7 @@ use std::net::SocketAddr; use jsonrpsee::client_transport::ws::{Uri, WsTransportClientBuilder}; use jsonrpsee::core::client::{Client, ClientBuilder, ClientT}; +use jsonrpsee::rpc_params; use jsonrpsee::ws_server::{RpcModule, WsServerBuilder}; #[tokio::main] @@ -42,7 +43,7 @@ async fn main() -> anyhow::Result<()> { let (tx, rx) = WsTransportClientBuilder::default().build(uri).await?; let client: Client = ClientBuilder::default().build_with_tokio(tx, rx); - let response: String = client.request("say_hello", None).await?; + let response: String = client.request("say_hello", rpc_params![]).await?; tracing::info!("response: {:?}", response); Ok(()) diff --git a/examples/examples/http.rs b/examples/examples/http.rs index 030e96356f..7350c5f9fb 100644 --- a/examples/examples/http.rs +++ b/examples/examples/http.rs @@ -42,7 +42,7 @@ async fn main() -> anyhow::Result<()> { let url = format!("http://{}", server_addr); let client = HttpClientBuilder::default().build(url)?; - let params = rpc_params!(1_u64, 2, 3); + let params = rpc_params![1_u64, 2, 3]; let response: Result = client.request("say_hello", params).await; tracing::info!("r: {:?}", response); diff --git a/examples/examples/http_middleware.rs b/examples/examples/http_middleware.rs index dfa3f1d1e4..f2b95ced07 100644 --- a/examples/examples/http_middleware.rs +++ b/examples/examples/http_middleware.rs @@ -37,6 +37,7 @@ use tower_http::LatencyUnit; use jsonrpsee::core::client::ClientT; use jsonrpsee::http_client::HttpClientBuilder; use jsonrpsee::http_server::{HttpServerBuilder, HttpServerHandle, RpcModule}; +use jsonrpsee::rpc_params; #[tokio::main] async fn main() -> anyhow::Result<()> { @@ -49,10 +50,10 @@ async fn main() -> anyhow::Result<()> { let url = format!("http://{}", addr); let client = HttpClientBuilder::default().build(&url)?; - let response: String = client.request("say_hello", None).await?; + let response: String = client.request("say_hello", rpc_params![]).await?; println!("[main]: response: {:?}", response); - let _response: Result = client.request("unknown_method", None).await; - let _ = client.request::("say_hello", None).await?; + let _response: Result = client.request("unknown_method", rpc_params![]).await; + let _: String = client.request("say_hello", rpc_params![]).await?; Ok(()) } diff --git a/examples/examples/http_proxy_middleware.rs b/examples/examples/http_proxy_middleware.rs index 53dd2d15b8..a04bd005f3 100644 --- a/examples/examples/http_proxy_middleware.rs +++ b/examples/examples/http_proxy_middleware.rs @@ -45,6 +45,7 @@ use jsonrpsee::core::client::ClientT; use jsonrpsee::http_client::HttpClientBuilder; use jsonrpsee::http_server::middleware::proxy_get_request::ProxyGetRequestLayer; use jsonrpsee::http_server::{HttpServerBuilder, HttpServerHandle, RpcModule}; +use jsonrpsee::rpc_params; #[tokio::main] async fn main() -> anyhow::Result<()> { @@ -58,7 +59,7 @@ async fn main() -> anyhow::Result<()> { // Use RPC client to get the response of `say_hello` method. let client = HttpClientBuilder::default().build(&url)?; - let response: String = client.request("say_hello", None).await?; + let response: String = client.request("say_hello", rpc_params![]).await?; println!("[main]: response: {:?}", response); // Use hyper client to manually submit a `GET /health` request. diff --git a/examples/examples/logger_http.rs b/examples/examples/logger_http.rs index 6803d6a9da..342d41f2c6 100644 --- a/examples/examples/logger_http.rs +++ b/examples/examples/logger_http.rs @@ -31,6 +31,7 @@ use jsonrpsee::core::client::ClientT; use jsonrpsee::core::logger::{self, Body, MethodKind, Params, Request}; use jsonrpsee::http_client::HttpClientBuilder; use jsonrpsee::http_server::{HttpServerBuilder, HttpServerHandle, RpcModule}; +use jsonrpsee::rpc_params; #[derive(Clone)] struct Timings; @@ -67,10 +68,10 @@ async fn main() -> anyhow::Result<()> { let url = format!("http://{}", addr); let client = HttpClientBuilder::default().build(&url)?; - let response: String = client.request("say_hello", None).await?; + let response: String = client.request("say_hello", rpc_params![]).await?; println!("response: {:?}", response); - let _response: Result = client.request("unknown_method", None).await; - let _ = client.request::("say_hello", None).await?; + let _response: Result = client.request("unknown_method", rpc_params![]).await; + let _: String = client.request("say_hello", rpc_params![]).await?; Ok(()) } diff --git a/examples/examples/logger_ws.rs b/examples/examples/logger_ws.rs index 9ae4d8f11d..cf8220c79e 100644 --- a/examples/examples/logger_ws.rs +++ b/examples/examples/logger_ws.rs @@ -29,6 +29,7 @@ use std::time::Instant; use jsonrpsee::core::client::ClientT; use jsonrpsee::core::logger::{self, Headers, MethodKind, Params}; +use jsonrpsee::rpc_params; use jsonrpsee::ws_client::WsClientBuilder; use jsonrpsee::ws_server::{RpcModule, WsServerBuilder}; @@ -75,10 +76,10 @@ async fn main() -> anyhow::Result<()> { let url = format!("ws://{}", addr); let client = WsClientBuilder::default().build(&url).await?; - let response: String = client.request("say_hello", None).await?; + let response: String = client.request("say_hello", rpc_params![]).await?; println!("response: {:?}", response); - let _response: Result = client.request("unknown_method", None).await; - let _ = client.request::("say_hello", None).await?; + let _response: Result = client.request("unknown_method", rpc_params![]).await; + let _: String = client.request("say_hello", rpc_params![]).await?; Ok(()) } diff --git a/examples/examples/multi_logger.rs b/examples/examples/multi_logger.rs index 651789d36c..360de2ba5a 100644 --- a/examples/examples/multi_logger.rs +++ b/examples/examples/multi_logger.rs @@ -132,11 +132,11 @@ async fn main() -> anyhow::Result<()> { let url = format!("ws://{}", addr); let client = WsClientBuilder::default().build(&url).await?; - let response: String = client.request("say_hello", None).await?; + let response: String = client.request("say_hello", rpc_params![]).await?; println!("response: {:?}", response); - let _response: Result = client.request("unknown_method", None).await; - let _ = client.request::("say_hello", None).await?; - client.request::<()>("thready", rpc_params![4]).await?; + let _response: Result = client.request("unknown_method", rpc_params![]).await; + let _: String = client.request("say_hello", rpc_params![]).await?; + client.request("thready", rpc_params![4]).await?; Ok(()) } diff --git a/examples/examples/proc_macro.rs b/examples/examples/proc_macro.rs index f980196899..e0b0aa8abc 100644 --- a/examples/examples/proc_macro.rs +++ b/examples/examples/proc_macro.rs @@ -1,101 +1,17 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any -// person obtaining a copy of this software and associated -// documentation files (the "Software"), to deal in the -// Software without restriction, including without -// limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software -// is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice -// shall be included in all copies or substantial portions -// of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. +//! Example of using proc macro to generate working client and server. -use std::net::SocketAddr; +use jsonrpsee::{core::RpcResult, proc_macros::rpc}; -use jsonrpsee::core::{async_trait, client::Subscription, Error}; -use jsonrpsee::proc_macros::rpc; -use jsonrpsee::types::SubscriptionResult; -use jsonrpsee::ws_client::WsClientBuilder; -use jsonrpsee::ws_server::{SubscriptionSink, WsServerBuilder, WsServerHandle}; +#[rpc(client)] +pub trait Rpc { + #[method(name = "foo")] + async fn async_method(&self, param_a: u8, param_b: String) -> RpcResult; -type ExampleHash = [u8; 32]; -type ExampleStorageKey = Vec; + // #[method(name = "bar")] + // fn sync_method(&self) -> RpcResult; -#[rpc(server, client, namespace = "state")] -pub trait Rpc -where - Hash: std::fmt::Debug, -{ - /// Async method call example. - #[method(name = "getKeys")] - async fn storage_keys(&self, storage_key: StorageKey, hash: Option) -> Result, Error>; - - /// Subscription that takes a `StorageKey` as input and produces a `Vec`. - #[subscription(name = "subscribeStorage" => "override", item = Vec)] - fn subscribe_storage(&self, keys: Option>); + // #[subscription(name = "subscribe", item = String)] + // fn sub(&self); } -pub struct RpcServerImpl; - -#[async_trait] -impl RpcServer for RpcServerImpl { - async fn storage_keys( - &self, - storage_key: ExampleStorageKey, - _hash: Option, - ) -> Result, Error> { - Ok(vec![storage_key]) - } - - // Note that the server's subscription method must return `SubscriptionResult`. - fn subscribe_storage( - &self, - mut sink: SubscriptionSink, - _keys: Option>, - ) -> SubscriptionResult { - let _ = sink.send(&vec![[0; 32]]); - Ok(()) - } -} - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - tracing_subscriber::FmtSubscriber::builder() - .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) - .try_init() - .expect("setting default subscriber failed"); - - let (server_addr, _handle) = run_server().await?; - let url = format!("ws://{}", server_addr); - - let client = WsClientBuilder::default().build(&url).await?; - assert_eq!(client.storage_keys(vec![1, 2, 3, 4], None::).await.unwrap(), vec![vec![1, 2, 3, 4]]); - - let mut sub: Subscription> = - RpcClient::::subscribe_storage(&client, None).await.unwrap(); - assert_eq!(Some(vec![[0; 32]]), sub.next().await.transpose().unwrap()); - - Ok(()) -} - -async fn run_server() -> anyhow::Result<(SocketAddr, WsServerHandle)> { - let server = WsServerBuilder::default().build("127.0.0.1:0").await?; - - let addr = server.local_addr()?; - let handle = server.start(RpcServerImpl.into_rpc())?; - Ok((addr, handle)) -} +fn main() {} diff --git a/examples/examples/ws.rs b/examples/examples/ws.rs index 7f27351c38..4f8e9b6fb7 100644 --- a/examples/examples/ws.rs +++ b/examples/examples/ws.rs @@ -27,6 +27,7 @@ use std::net::SocketAddr; use jsonrpsee::core::client::ClientT; +use jsonrpsee::rpc_params; use jsonrpsee::ws_client::WsClientBuilder; use jsonrpsee::ws_server::{RpcModule, WsServerBuilder}; use tracing_subscriber::util::SubscriberInitExt; @@ -42,7 +43,7 @@ async fn main() -> anyhow::Result<()> { let url = format!("ws://{}", addr); let client = WsClientBuilder::default().build(&url).await?; - let response: String = client.request("say_hello", None).await?; + let response: String = client.request("say_hello", rpc_params![]).await?; tracing::info!("response: {:?}", response); Ok(()) diff --git a/examples/examples/ws_pubsub_with_params.rs b/examples/examples/ws_pubsub_with_params.rs index 8eb22be9b4..16ab365f08 100644 --- a/examples/examples/ws_pubsub_with_params.rs +++ b/examples/examples/ws_pubsub_with_params.rs @@ -28,7 +28,7 @@ use std::net::SocketAddr; use std::time::Duration; use futures::StreamExt; -use jsonrpsee::core::client::SubscriptionClientT; +use jsonrpsee::core::client::{Subscription, SubscriptionClientT}; use jsonrpsee::core::error::SubscriptionClosed; use jsonrpsee::rpc_params; use jsonrpsee::ws_client::WsClientBuilder; @@ -49,13 +49,13 @@ async fn main() -> anyhow::Result<()> { let client = WsClientBuilder::default().build(&url).await?; // Subscription with a single parameter - let mut sub_params_one = - client.subscribe::>("sub_one_param", rpc_params![3], "unsub_one_param").await?; + let mut sub_params_one: Subscription> = + client.subscribe("sub_one_param", rpc_params![3], "unsub_one_param").await?; tracing::info!("subscription with one param: {:?}", sub_params_one.next().await); // Subscription with multiple parameters - let mut sub_params_two = - client.subscribe::("sub_params_two", rpc_params![2, 5], "unsub_params_two").await?; + let mut sub_params_two: Subscription = + client.subscribe("sub_params_two", rpc_params![2, 5], "unsub_params_two").await?; tracing::info!("subscription with two params: {:?}", sub_params_two.next().await); Ok(()) diff --git a/jsonrpsee/src/lib.rs b/jsonrpsee/src/lib.rs index 7aaae99a97..2f906522a3 100644 --- a/jsonrpsee/src/lib.rs +++ b/jsonrpsee/src/lib.rs @@ -103,5 +103,5 @@ cfg_client_or_server! { } cfg_client! { - pub use jsonrpsee_core::rpc_params; + pub use jsonrpsee_types::{rpc_params, ToRpcParams}; } diff --git a/proc-macros/tests/ui/correct/basic.rs b/proc-macros/tests/ui/correct/basic.rs index 98e48b0d65..c538d467e2 100644 --- a/proc-macros/tests/ui/correct/basic.rs +++ b/proc-macros/tests/ui/correct/basic.rs @@ -5,7 +5,7 @@ use std::net::SocketAddr; use jsonrpsee::core::{async_trait, client::ClientT, RpcResult}; use jsonrpsee::proc_macros::rpc; use jsonrpsee::rpc_params; -use jsonrpsee::types::SubscriptionResult; +use jsonrpsee::types::{SubscriptionResult, UnnamedParams}; use jsonrpsee::ws_client::*; use jsonrpsee::ws_server::{SubscriptionSink, WsServerBuilder}; @@ -103,11 +103,10 @@ async fn main() { assert_eq!(client.optional_params(Some(1), "a".into()).await.unwrap(), true); assert_eq!(client.array_params(vec![1]).await.unwrap(), 1); - assert_eq!(client.request::("foo_array_params", rpc_params![Vec::::new()]).await.unwrap(), 0); + assert_eq!(client.request::("foo_array_params", rpc_params![Vec::::new()]).await.unwrap(), 0); - assert_eq!(client.request::("foo_optional_param", rpc_params![]).await.unwrap(), false); - assert_eq!(client.request::("foo_optional_param", None).await.unwrap(), false); - assert_eq!(client.request::("foo_optional_param", rpc_params![1]).await.unwrap(), true); + assert_eq!(client.request::("foo_optional_param", rpc_params![]).await.unwrap(), false); + assert_eq!(client.request::("foo_optional_param", rpc_params![1]).await.unwrap(), true); let mut sub = client.sub().await.unwrap(); let first_recv = sub.next().await.transpose().unwrap(); 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 ec2f0f0892..3ab57c2a92 100644 --- a/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.stderr +++ b/proc-macros/tests/ui/incorrect/rpc/rpc_empty_bounds.stderr @@ -21,6 +21,6 @@ error[E0277]: the trait bound `for<'de> ::Hash: Deserialize<'de> note: required by a bound in `request` --> $WORKSPACE/core/src/client/mod.rs | - | R: DeserializeOwned; + | R: DeserializeOwned, | ^^^^^^^^^^^^^^^^ required by this bound in `request` = note: this error originates in the attribute macro `rpc` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/tests/integration_tests.rs b/tests/tests/integration_tests.rs index 18fc9963a2..bfa214edde 100644 --- a/tests/tests/integration_tests.rs +++ b/tests/tests/integration_tests.rs @@ -40,6 +40,7 @@ use jsonrpsee::http_client::HttpClientBuilder; use jsonrpsee::http_server::AccessControlBuilder; use jsonrpsee::rpc_params; use jsonrpsee::types::error::ErrorObject; +use jsonrpsee::types::{BatchParamsBuilder, UnnamedParams}; use jsonrpsee::ws_client::WsClientBuilder; use tokio::time::interval; use tokio_stream::wrappers::IntervalStream; @@ -61,8 +62,9 @@ async fn ws_subscription_works() { let server_url = format!("ws://{}", server_addr); let client = WsClientBuilder::default().build(&server_url).await.unwrap(); let mut hello_sub: Subscription = - client.subscribe("subscribe_hello", None, "unsubscribe_hello").await.unwrap(); - let mut foo_sub: Subscription = client.subscribe("subscribe_foo", None, "unsubscribe_foo").await.unwrap(); + client.subscribe("subscribe_hello", rpc_params![], "unsubscribe_hello").await.unwrap(); + let mut foo_sub: Subscription = + client.subscribe("subscribe_foo", rpc_params![], "unsubscribe_foo").await.unwrap(); for _ in 0..10 { let hello = hello_sub.next().await.unwrap().unwrap(); @@ -80,7 +82,8 @@ async fn ws_unsubscription_works() { let server_url = format!("ws://{}", server_addr); let client = WsClientBuilder::default().max_concurrent_requests(1).build(&server_url).await.unwrap(); - let mut sub: Subscription = client.subscribe("subscribe_foo", None, "unsubscribe_foo").await.unwrap(); + let mut sub: Subscription = + client.subscribe("subscribe_foo", rpc_params![], "unsubscribe_foo").await.unwrap(); // It's technically possible to have race-conditions between the notifications and the unsubscribe message. // So let's wait for the first notification and then unsubscribe. @@ -93,7 +96,8 @@ async fn ws_unsubscription_works() { // Wait until a slot is available, as only one concurrent call is allowed. // Then when this finishes we know that unsubscribe call has been finished. for _ in 0..30 { - if client.request::("say_hello", rpc_params![]).await.is_ok() { + let res: Result = client.request("say_hello", rpc_params![]).await; + if res.is_ok() { success = true; break; } @@ -126,7 +130,7 @@ async fn ws_method_call_works() { let server_addr = websocket_server().await; let server_url = format!("ws://{}", server_addr); let client = WsClientBuilder::default().build(&server_url).await.unwrap(); - let response: String = client.request("say_hello", None).await.unwrap(); + let response: String = client.request("say_hello", rpc_params![]).await.unwrap(); assert_eq!(&response, "hello"); } @@ -137,7 +141,7 @@ async fn ws_method_call_str_id_works() { let server_addr = websocket_server().await; let server_url = format!("ws://{}", server_addr); let client = WsClientBuilder::default().id_format(IdKind::String).build(&server_url).await.unwrap(); - let response: String = client.request("say_hello", None).await.unwrap(); + let response: String = client.request("say_hello", rpc_params![]).await.unwrap(); assert_eq!(&response, "hello"); } @@ -148,7 +152,7 @@ async fn http_method_call_works() { let (server_addr, _handle) = http_server().await; let uri = format!("http://{}", server_addr); let client = HttpClientBuilder::default().build(&uri).unwrap(); - let response: String = client.request("say_hello", None).await.unwrap(); + let response: String = client.request("say_hello", rpc_params![]).await.unwrap(); assert_eq!(&response, "hello"); } @@ -159,7 +163,7 @@ async fn http_method_call_str_id_works() { let (server_addr, _handle) = http_server().await; let uri = format!("http://{}", server_addr); let client = HttpClientBuilder::default().id_format(IdKind::String).build(&uri).unwrap(); - let response: String = client.request("say_hello", None).await.unwrap(); + let response: String = client.request("say_hello", rpc_params![]).await.unwrap(); assert_eq!(&response, "hello"); } @@ -171,8 +175,10 @@ async fn http_concurrent_method_call_limits_works() { let uri = format!("http://{}", server_addr); let client = HttpClientBuilder::default().max_concurrent_requests(1).build(&uri).unwrap(); - let (first, second) = - tokio::join!(client.request::("say_hello", None), client.request::("say_hello", None),); + let (first, second) = tokio::join!( + client.request::("say_hello", rpc_params!()), + client.request::("say_hello", rpc_params![]), + ); assert!(first.is_ok()); assert!(matches!(second, Err(Error::MaxSlotsExceeded))); @@ -189,9 +195,9 @@ async fn ws_subscription_several_clients() { for _ in 0..10 { let client = WsClientBuilder::default().build(&server_url).await.unwrap(); let hello_sub: Subscription = - client.subscribe("subscribe_hello", None, "unsubscribe_hello").await.unwrap(); + client.subscribe("subscribe_hello", rpc_params![], "unsubscribe_hello").await.unwrap(); let foo_sub: Subscription = - client.subscribe("subscribe_foo", None, "unsubscribe_foo").await.unwrap(); + client.subscribe("subscribe_foo", rpc_params![], "unsubscribe_foo").await.unwrap(); clients.push((client, hello_sub, foo_sub)) } } @@ -208,8 +214,9 @@ async fn ws_subscription_several_clients_with_drop() { let client = WsClientBuilder::default().max_notifs_per_subscription(u32::MAX as usize).build(&server_url).await.unwrap(); let hello_sub: Subscription = - client.subscribe("subscribe_hello", None, "unsubscribe_hello").await.unwrap(); - let foo_sub: Subscription = client.subscribe("subscribe_foo", None, "unsubscribe_foo").await.unwrap(); + client.subscribe("subscribe_hello", rpc_params![], "unsubscribe_hello").await.unwrap(); + let foo_sub: Subscription = + client.subscribe("subscribe_foo", rpc_params![], "unsubscribe_foo").await.unwrap(); clients.push((client, hello_sub, foo_sub)) } @@ -253,7 +260,7 @@ async fn ws_subscription_without_polling_doesnt_make_client_unuseable() { let client = WsClientBuilder::default().max_notifs_per_subscription(4).build(&server_url).await.unwrap(); let mut hello_sub: Subscription = - client.subscribe("subscribe_hello", None, "unsubscribe_hello").await.unwrap(); + client.subscribe("subscribe_hello", rpc_params![], "unsubscribe_hello").await.unwrap(); // don't poll the subscription stream for 2 seconds, should be full now. tokio::time::sleep(Duration::from_secs(2)).await; @@ -267,11 +274,11 @@ async fn ws_subscription_without_polling_doesnt_make_client_unuseable() { assert!(hello_sub.next().await.is_none()); // The client should still be useable => make sure it still works. - let _hello_req: JsonValue = client.request("say_hello", None).await.unwrap(); + let _hello_req: JsonValue = client.request("say_hello", rpc_params![]).await.unwrap(); // The same subscription should be possible to register again. let mut other_sub: Subscription = - client.subscribe("subscribe_hello", None, "unsubscribe_hello").await.unwrap(); + client.subscribe("subscribe_hello", rpc_params![], "unsubscribe_hello").await.unwrap(); other_sub.next().await.unwrap().unwrap(); } @@ -288,7 +295,7 @@ async fn ws_making_more_requests_than_allowed_should_not_deadlock() { for _ in 0..6 { let c = client.clone(); - requests.push(tokio::spawn(async move { c.request::("say_hello", None).await })); + requests.push(tokio::spawn(async move { c.request::("say_hello", rpc_params![]).await })); } for req in requests { @@ -309,7 +316,7 @@ async fn http_making_more_requests_than_allowed_should_not_deadlock() { for _ in 0..6 { let c = client.clone(); - requests.push(tokio::spawn(async move { c.request::("say_hello", None).await })); + requests.push(tokio::spawn(async move { c.request::("say_hello", rpc_params![]).await })); } for req in requests { @@ -322,7 +329,7 @@ async fn https_works() { init_logger(); let client = HttpClientBuilder::default().build("https://kusama-rpc.polkadot.io:443").unwrap(); - let response: String = client.request("system_chain", None).await.unwrap(); + let response: String = client.request("system_chain", rpc_params![]).await.unwrap(); assert_eq!(&response, "Kusama"); } @@ -331,7 +338,7 @@ async fn wss_works() { init_logger(); let client = WsClientBuilder::default().build("wss://kusama-rpc.polkadot.io:443").await.unwrap(); - let response: String = client.request("system_chain", None).await.unwrap(); + let response: String = client.request("system_chain", rpc_params![]).await.unwrap(); assert_eq!(&response, "Kusama"); } @@ -360,9 +367,11 @@ async fn ws_unsubscribe_releases_request_slots() { let client = WsClientBuilder::default().max_concurrent_requests(1).build(&server_url).await.unwrap(); - let sub1: Subscription = client.subscribe("subscribe_hello", None, "unsubscribe_hello").await.unwrap(); + let sub1: Subscription = + client.subscribe("subscribe_hello", rpc_params![], "unsubscribe_hello").await.unwrap(); drop(sub1); - let _: Subscription = client.subscribe("subscribe_hello", None, "unsubscribe_hello").await.unwrap(); + let _: Subscription = + client.subscribe("subscribe_hello", rpc_params![], "unsubscribe_hello").await.unwrap(); } #[tokio::test] @@ -374,7 +383,8 @@ async fn server_should_be_able_to_close_subscriptions() { let client = WsClientBuilder::default().build(&server_url).await.unwrap(); - let mut sub: Subscription = client.subscribe("subscribe_noop", None, "unsubscribe_noop").await.unwrap(); + let mut sub: Subscription = + client.subscribe("subscribe_noop", rpc_params![], "unsubscribe_noop").await.unwrap(); assert!(sub.next().await.is_none()); } @@ -388,13 +398,15 @@ async fn ws_close_pending_subscription_when_server_terminated() { let c1 = WsClientBuilder::default().build(&server_url).await.unwrap(); - let mut sub: Subscription = c1.subscribe("subscribe_hello", None, "unsubscribe_hello").await.unwrap(); + let mut sub: Subscription = + c1.subscribe("subscribe_hello", rpc_params![], "unsubscribe_hello").await.unwrap(); assert!(matches!(sub.next().await, Some(Ok(_)))); handle.stop().unwrap().await; - let sub2: Result, _> = c1.subscribe("subscribe_hello", None, "unsubscribe_hello").await; + let sub2: Result, _> = + c1.subscribe("subscribe_hello", rpc_params![], "unsubscribe_hello").await; // no new request should be accepted. assert!(matches!(sub2, Err(_))); @@ -446,7 +458,8 @@ async fn ws_server_should_stop_subscription_after_client_drop() { let client = WsClientBuilder::default().build(&server_url).await.unwrap(); - let mut sub: Subscription = client.subscribe("subscribe_hello", None, "unsubscribe_hello").await.unwrap(); + let mut sub: Subscription = + client.subscribe("subscribe_hello", rpc_params![], "unsubscribe_hello").await.unwrap(); let res = sub.next().await.unwrap().unwrap(); @@ -474,7 +487,7 @@ async fn ws_server_notify_client_on_disconnect() { tokio::spawn(async move { let client = WsClientBuilder::default().build(&server_url).await.unwrap(); // Validate server is up. - client.request::("say_hello", None).await.unwrap(); + client.request::("say_hello", rpc_params![]).await.unwrap(); // Signal client is waiting for the server to disconnect. up_tx.send(()).unwrap(); @@ -521,7 +534,7 @@ async fn ws_server_notify_client_on_disconnect_with_closed_server() { let client = WsClientBuilder::default().build(&server_url).await.unwrap(); // Validate server is up. - client.request::("say_hello", None).await.unwrap(); + client.request::("say_hello", rpc_params![]).await.unwrap(); // Stop the server. server_handle.stop().unwrap().await; @@ -541,7 +554,7 @@ async fn ws_server_cancels_subscriptions_on_reset_conn() { let mut subs = Vec::new(); for _ in 0..10 { - subs.push(client.subscribe::("subscribe_sleep", None, "unsubscribe_sleep").await.unwrap()); + subs.push(client.subscribe::("subscribe_sleep", rpc_params![], "unsubscribe_sleep").await.unwrap()); } // terminate connection. @@ -560,8 +573,10 @@ async fn ws_server_cancels_sub_stream_after_err() { let server_url = format!("ws://{}", addr); let client = WsClientBuilder::default().build(&server_url).await.unwrap(); - let mut sub: Subscription = - client.subscribe("subscribe_with_err_on_stream", None, "unsubscribe_with_err_on_stream").await.unwrap(); + let mut sub: Subscription = client + .subscribe("subscribe_with_err_on_stream", rpc_params![], "unsubscribe_with_err_on_stream") + .await + .unwrap(); assert_eq!(sub.next().await.unwrap().unwrap(), 1); // The server closed down the subscription with the underlying error from the stream. @@ -576,8 +591,10 @@ async fn ws_server_subscribe_with_stream() { let server_url = format!("ws://{}", addr); let client = WsClientBuilder::default().build(&server_url).await.unwrap(); - let mut sub1: Subscription = client.subscribe("subscribe_5_ints", None, "unsubscribe_5_ints").await.unwrap(); - let mut sub2: Subscription = client.subscribe("subscribe_5_ints", None, "unsubscribe_5_ints").await.unwrap(); + let mut sub1: Subscription = + client.subscribe("subscribe_5_ints", rpc_params![], "unsubscribe_5_ints").await.unwrap(); + let mut sub2: Subscription = + client.subscribe("subscribe_5_ints", rpc_params![], "unsubscribe_5_ints").await.unwrap(); let (r1, r2) = futures::future::try_join( sub1.by_ref().take(2).try_collect::>(), @@ -609,7 +626,7 @@ async fn ws_server_pipe_from_stream_should_cancel_tasks_immediately() { let mut subs = Vec::new(); for _ in 0..10 { - subs.push(client.subscribe::("subscribe_sleep", None, "unsubscribe_sleep").await.unwrap()) + subs.push(client.subscribe::("subscribe_sleep", rpc_params![], "unsubscribe_sleep").await.unwrap()) } // This will call the `unsubscribe method`. @@ -626,7 +643,8 @@ async fn ws_server_pipe_from_stream_can_be_reused() { let (addr, _handle) = websocket_server_with_subscription().await; let client = WsClientBuilder::default().build(&format!("ws://{}", addr)).await.unwrap(); - let sub = client.subscribe::("can_reuse_subscription", None, "u_can_reuse_subscription").await.unwrap(); + let sub = + client.subscribe::("can_reuse_subscription", rpc_params![], "u_can_reuse_subscription").await.unwrap(); let items = sub.fold(0, |acc, _| async move { acc + 1 }).await; @@ -641,12 +659,11 @@ async fn ws_batch_works() { let server_url = format!("ws://{}", server_addr); let client = WsClientBuilder::default().build(&server_url).await.unwrap(); - let mut batch = Vec::new(); - - batch.push(("say_hello", rpc_params![])); - batch.push(("slow_hello", rpc_params![])); + let mut batch = BatchParamsBuilder::new(); + batch.insert("say_hello", rpc_params![]).unwrap(); + batch.insert("slow_hello", rpc_params![]).unwrap(); - let responses: Vec = client.batch_request(batch).await.unwrap(); + let responses: Vec = client.batch_request(batch.build()).await.unwrap(); assert_eq!(responses, vec!["hello".to_string(), "hello".to_string()]); } @@ -688,12 +705,12 @@ async fn ws_server_limit_subs_per_conn_works() { let mut subs2 = Vec::new(); for _ in 0..10 { - subs1.push(c1.subscribe::("subscribe_forever", None, "unsubscribe_forever").await.unwrap()); - subs2.push(c2.subscribe::("subscribe_forever", None, "unsubscribe_forever").await.unwrap()); + subs1.push(c1.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever").await.unwrap()); + subs2.push(c2.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever").await.unwrap()); } - let err1 = c1.subscribe::("subscribe_forever", None, "unsubscribe_forever").await; - let err2 = c1.subscribe::("subscribe_forever", None, "unsubscribe_forever").await; + let err1 = c1.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever").await; + let err2 = c1.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever").await; let data = "\"Exceeded max limit of 10\""; @@ -741,7 +758,9 @@ async fn ws_server_unsub_methods_should_ignore_sub_limit() { // Add 10 subscriptions (this should fill our subscrition limit for this connection): let mut subs = Vec::new(); for _ in 0..10 { - subs.push(client.subscribe::("subscribe_forever", None, "unsubscribe_forever").await.unwrap()); + subs.push( + client.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever").await.unwrap(), + ); } // Get the ID of one of them: @@ -913,7 +932,7 @@ async fn ws_subscribe_with_bad_params() { let client = WsClientBuilder::default().build(&server_url).await.unwrap(); let err = client - .subscribe::("subscribe_add_one", rpc_params!["0x0"], "unsubscribe_add_one") + .subscribe::("subscribe_add_one", rpc_params!["0x0"], "unsubscribe_add_one") .await .unwrap_err(); assert!(matches!(err, Error::Call(_))); @@ -961,7 +980,7 @@ async fn ws_host_filtering_wildcard_works() { let server_url = format!("ws://{}", addr); let client = WsClientBuilder::default().build(&server_url).await.unwrap(); - assert!(client.request::("say_hello", None).await.is_ok()); + assert!(client.request::("say_hello", rpc_params![]).await.is_ok()); } #[tokio::test] @@ -985,5 +1004,5 @@ async fn http_host_filtering_wildcard_works() { let server_url = format!("http://{}", addr); let client = HttpClientBuilder::default().build(&server_url).unwrap(); - assert!(client.request::("say_hello", None).await.is_ok()); + assert!(client.request::("say_hello", rpc_params![]).await.is_ok()); } diff --git a/tests/tests/metrics.rs b/tests/tests/metrics.rs index be442eec97..0f090bca59 100644 --- a/tests/tests/metrics.rs +++ b/tests/tests/metrics.rs @@ -38,7 +38,7 @@ use jsonrpsee::proc_macros::rpc; use jsonrpsee::types::Params; use jsonrpsee::ws_client::WsClientBuilder; use jsonrpsee::ws_server::{WsServerBuilder, WsServerHandle}; -use jsonrpsee::RpcModule; +use jsonrpsee::{rpc_params, RpcModule}; use tokio::time::sleep; #[derive(Clone, Default)] @@ -177,14 +177,19 @@ async fn ws_server_logger() { let server_url = format!("ws://{}", server_addr); let client = WsClientBuilder::default().build(&server_url).await.unwrap(); - assert_eq!(client.request::("say_hello", None).await.unwrap(), "hello"); + let res: String = client.request("say_hello", rpc_params![]).await.unwrap(); + assert_eq!(res, "hello"); - assert!(client.request::("unknown_method", None).await.is_err()); + let res: Result = client.request("unknown_method", rpc_params![]).await; + assert!(res.is_err()); - assert_eq!(client.request::("say_hello", None).await.unwrap(), "hello"); - assert_eq!(client.request::("say_hello", None).await.unwrap(), "hello"); + let res: String = client.request("say_hello", rpc_params![]).await.unwrap(); + assert_eq!(res, "hello"); + let res: String = client.request("say_hello", rpc_params![]).await.unwrap(); + assert_eq!(res, "hello"); - assert!(client.request::("unknown_method", None).await.is_err()); + let res: Result = client.request("unknown_method", rpc_params![]).await; + assert!(res.is_err()); { let inner = counter.inner.lock().unwrap(); @@ -208,14 +213,19 @@ async fn http_server_logger() { let server_url = format!("http://{}", server_addr); let client = HttpClientBuilder::default().build(&server_url).unwrap(); - assert_eq!(client.request::("say_hello", None).await.unwrap(), "hello"); + let res: String = client.request("say_hello", rpc_params![]).await.unwrap(); + assert_eq!(res, "hello"); - assert!(client.request::("unknown_method", None).await.is_err()); + let res: Result = client.request("unknown_method", rpc_params![]).await; + assert!(res.is_err()); - assert_eq!(client.request::("say_hello", None).await.unwrap(), "hello"); - assert_eq!(client.request::("say_hello", None).await.unwrap(), "hello"); + let res: String = client.request("say_hello", rpc_params![]).await.unwrap(); + assert_eq!(res, "hello"); + let res: String = client.request("say_hello", rpc_params![]).await.unwrap(); + assert_eq!(res, "hello"); - assert!(client.request::("unknown_method", None).await.is_err()); + let res: Result = client.request("unknown_method", rpc_params![]).await; + assert!(res.is_err()); { let inner = counter.inner.lock().unwrap(); diff --git a/tests/tests/proc_macros.rs b/tests/tests/proc_macros.rs index 3f110dc259..4f72421b9b 100644 --- a/tests/tests/proc_macros.rs +++ b/tests/tests/proc_macros.rs @@ -26,7 +26,6 @@ //! Example of using proc macro to generate working client and server. -use std::collections::BTreeMap; use std::net::SocketAddr; use jsonrpsee::core::client::ClientT; @@ -35,7 +34,7 @@ use jsonrpsee::http_client::HttpClientBuilder; use jsonrpsee::http_server::HttpServerBuilder; use jsonrpsee::rpc_params; use jsonrpsee::types::error::{CallError, ErrorCode}; -use jsonrpsee::types::ParamsSer; + use jsonrpsee::ws_client::*; use jsonrpsee::ws_server::WsServerBuilder; use serde_json::json; @@ -197,6 +196,7 @@ mod rpc_impl { } // Use generated implementations of server and client. +use crate::types::{NamedParams, NamedParamsBuilder, UnnamedParams}; use rpc_impl::{RpcClient, RpcServer, RpcServerImpl}; pub async fn websocket_server() -> SocketAddr { @@ -340,8 +340,8 @@ async fn calls_with_bad_params() { let client = WsClientBuilder::default().build(&server_url).await.unwrap(); // Sub with faulty params as array. - let err = client - .subscribe::("foo_echo", rpc_params!["0x0"], "foo_unsubscribe_echo") + let err: Error = client + .subscribe::("foo_echo", rpc_params!["0x0"], "foo_unsubscribe_echo") .await .unwrap_err(); assert!( @@ -349,28 +349,30 @@ async fn calls_with_bad_params() { ); // Call with faulty params as array. - let err = client.request::("foo_foo", rpc_params!["faulty", "ok"]).await.unwrap_err(); + 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()) ); // Sub with faulty params as map. - let mut map = BTreeMap::new(); - map.insert("val", "0x0".into()); - let params = ParamsSer::Map(map); - let err = - client.subscribe::("foo_echo", Some(params), "foo_unsubscribe_echo").await.unwrap_err(); + let mut params = NamedParamsBuilder::new(); + params.insert("val", "0x0").unwrap(); + let params = params.build(); + + 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()) ); // Call with faulty params as map. - let mut map = BTreeMap::new(); - map.insert("param_a", 1.into()); - map.insert("param_b", 99.into()); - let params = ParamsSer::Map(map); - let err = client.request::("foo_foo", Some(params)).await.unwrap_err(); + let mut params = NamedParamsBuilder::new(); + params.insert("param_a", 1).unwrap(); + params.insert("param_b", 2).unwrap(); + let params = params.build(); + + 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 `99`, expected a string") && err.code() == ErrorCode::InvalidParams.code()) + matches!(err, Error::Call(CallError::Custom (err)) if err.message().contains("invalid type: integer `2`, expected a string") && err.code() == ErrorCode::InvalidParams.code()) ); } diff --git a/tests/tests/resource_limiting.rs b/tests/tests/resource_limiting.rs index 029e28673e..6df15b12a2 100644 --- a/tests/tests/resource_limiting.rs +++ b/tests/tests/resource_limiting.rs @@ -35,9 +35,10 @@ use jsonrpsee::http_server::{HttpServerBuilder, HttpServerHandle}; use jsonrpsee::proc_macros::rpc; use jsonrpsee::types::error::CallError; use jsonrpsee::types::SubscriptionResult; + use jsonrpsee::ws_client::WsClientBuilder; use jsonrpsee::ws_server::{WsServerBuilder, WsServerHandle}; -use jsonrpsee::{RpcModule, SubscriptionSink}; +use jsonrpsee::{rpc_params, RpcModule, SubscriptionSink}; use tokio::time::{interval, sleep}; use tokio_stream::wrappers::IntervalStream; @@ -185,10 +186,10 @@ async fn run_tests_on_ws_server(server_addr: SocketAddr, server_handle: WsServer // 2 CPU units (default) per call, so 4th call exceeds cap let (pass1, pass2, pass3, fail) = tokio::join!( - client.request::("say_hello", None), - client.request::("say_hello", None), - client.request::("say_hello", None), - client.request::("say_hello", None), + client.request::("say_hello", rpc_params!()), + client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params![]), ); assert!(pass1.is_ok()); @@ -198,11 +199,11 @@ async fn run_tests_on_ws_server(server_addr: SocketAddr, server_handle: WsServer // 3 CPU units per call, so 3rd call exceeds CPU cap, but we can still get on MEM let (pass_cpu1, pass_cpu2, fail_cpu, pass_mem, fail_mem) = tokio::join!( - client.request::("expensive_call", None), - client.request::("expensive_call", None), - client.request::("expensive_call", None), - client.request::("memory_hog", None), - client.request::("memory_hog", None), + client.request::("expensive_call", rpc_params![]), + client.request::("expensive_call", rpc_params![]), + client.request::("expensive_call", rpc_params![]), + client.request::("memory_hog", rpc_params![]), + client.request::("memory_hog", rpc_params![]), ); assert!(pass_cpu1.is_ok()); @@ -216,8 +217,8 @@ async fn run_tests_on_ws_server(server_addr: SocketAddr, server_handle: WsServer // // Thus, we can't assume that all subscriptions drop their resources instantly anymore. let (pass1, pass2) = tokio::join!( - client.subscribe::("subscribe_hello", None, "unsubscribe_hello"), - client.subscribe::("subscribe_hello", None, "unsubscribe_hello"), + client.subscribe::("subscribe_hello", rpc_params![], "unsubscribe_hello"), + client.subscribe::("subscribe_hello", rpc_params![], "unsubscribe_hello"), ); assert!(pass1.is_ok()); @@ -225,9 +226,9 @@ async fn run_tests_on_ws_server(server_addr: SocketAddr, server_handle: WsServer // 3 CPU units (manually set for subscriptions) per call, so 3th call exceeds cap let (pass1, pass2, fail) = tokio::join!( - client.subscribe::("subscribe_hello_limit", None, "unsubscribe_hello_limit"), - client.subscribe::("subscribe_hello_limit", None, "unsubscribe_hello_limit"), - client.subscribe::("subscribe_hello_limit", None, "unsubscribe_hello_limit"), + client.subscribe::("subscribe_hello_limit", rpc_params![], "unsubscribe_hello_limit"), + client.subscribe::("subscribe_hello_limit", rpc_params![], "unsubscribe_hello_limit"), + client.subscribe::("subscribe_hello_limit", rpc_params![], "unsubscribe_hello_limit"), ); assert!(pass1.is_ok()); @@ -243,10 +244,10 @@ async fn run_tests_on_http_server(server_addr: SocketAddr, server_handle: HttpSe // 2 CPU units (default) per call, so 4th call exceeds cap let (a, b, c, d) = tokio::join!( - client.request::("say_hello", None), - client.request::("say_hello", None), - client.request::("say_hello", None), - client.request::("say_hello", None), + client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params![]), ); // HTTP does not guarantee ordering diff --git a/types/src/params.rs b/types/src/params.rs index cc38d62064..b89af0b2d2 100644 --- a/types/src/params.rs +++ b/types/src/params.rs @@ -39,6 +39,13 @@ use serde::{Deserialize, Serialize}; use serde_json::value::RawValue; use serde_json::Value as JsonValue; +#[doc(hidden)] +pub mod __reexports { + pub use crate::params::ToRpcParams; + pub use crate::params::UnnamedParams; + pub use crate::params::UnnamedParamsBuilder; +} + /// JSON-RPC v2 marker type. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct TwoPointZero; From 315ee8938680215ac02244f50f112007c2fa25c6 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 26 Aug 2022 19:15:53 +0300 Subject: [PATCH 09/41] core: Move `rpc_params` to core and simplify testing Signed-off-by: Alexandru Vasile --- client/http-client/src/tests.rs | 5 ++-- client/ws-client/src/tests.rs | 4 ++-- core/src/client/mod.rs | 17 ++++++++------ jsonrpsee/src/lib.rs | 2 +- types/src/lib.rs | 7 ------ types/src/params.rs | 18 --------------- types/src/request.rs | 41 +++++++++++---------------------- 7 files changed, 29 insertions(+), 65 deletions(-) diff --git a/client/http-client/src/tests.rs b/client/http-client/src/tests.rs index cc79c4ad9e..c38a857d88 100644 --- a/client/http-client/src/tests.rs +++ b/client/http-client/src/tests.rs @@ -28,13 +28,14 @@ use crate::types::error::{ErrorCode, ErrorObject}; use crate::HttpClientBuilder; use jsonrpsee_core::client::{ClientT, IdKind}; -use serde_json::value::RawValue; +use jsonrpsee_core::rpc_params; use jsonrpsee_core::Error; 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::{rpc_params, BatchParamsBuilder}; +use jsonrpsee_types::BatchParamsBuilder; +use serde_json::value::RawValue; #[tokio::test] async fn method_call_works() { diff --git a/client/ws-client/src/tests.rs b/client/ws-client/src/tests.rs index e45aca8ead..ba89146a9a 100644 --- a/client/ws-client/src/tests.rs +++ b/client/ws-client/src/tests.rs @@ -30,12 +30,12 @@ use crate::types::error::{ErrorCode, ErrorObject}; use crate::WsClientBuilder; use jsonrpsee_core::client::{ClientT, SubscriptionClientT}; use jsonrpsee_core::client::{IdKind, Subscription}; -use jsonrpsee_core::Error; +use jsonrpsee_core::{rpc_params, 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::{rpc_params, BatchParamsBuilder}; +use jsonrpsee_types::BatchParamsBuilder; use serde_json::value::RawValue; use serde_json::Value as JsonValue; diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index 67833978bf..452518ff67 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -44,9 +44,12 @@ use serde_json::Value as JsonValue; use serde_json::value::RawValue; use jsonrpsee_types::params::ToRpcParams; +// Re-exports for the `rpc_params` macro. #[doc(hidden)] pub mod __reexports { - pub use crate::to_json_value; + // Needs to be in scope for `UnnamedParams` to implement it. + pub use jsonrpsee_types::ToRpcParams; + // Main builder object for constructing the rpc parameters. pub use jsonrpsee_types::UnnamedParamsBuilder; } @@ -179,21 +182,21 @@ pub trait TransportReceiverT: 'static { } #[macro_export] -/// Convert the given values to a [`jsonrpsee_types::ParamsSer`] as expected by a jsonrpsee Client (http or websocket). +/// Convert the given values to a [`jsonrpsee_types::UnnamedParams`] as expected by a jsonrpsee Client (http or websocket). macro_rules! rpc_params { + () => { + // ToRpcParams is implemented for the empty tuple. + () + }; ($($param:expr),*) => { { - let mut __params = UnnamedParamsBuilder::new(); + let mut __params = $crate::client::__reexports::UnnamedParamsBuilder::new(); $( __params.insert($param).expect("json serialization is infallible; qed."); )* __params.build() } }; - () => { - // ToRpcParams is implemented for the empty tuple. - () - } } /// Subscription kind diff --git a/jsonrpsee/src/lib.rs b/jsonrpsee/src/lib.rs index 2f906522a3..7aaae99a97 100644 --- a/jsonrpsee/src/lib.rs +++ b/jsonrpsee/src/lib.rs @@ -103,5 +103,5 @@ cfg_client_or_server! { } cfg_client! { - pub use jsonrpsee_types::{rpc_params, ToRpcParams}; + pub use jsonrpsee_core::rpc_params; } diff --git a/types/src/lib.rs b/types/src/lib.rs index 60874e64f5..6449f9b57f 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -52,10 +52,3 @@ pub use response::{Response, SubscriptionPayload, SubscriptionResponse}; /// Empty `RpcParams` type. pub type EmptyParams = Vec<()>; - -#[doc(hidden)] -pub mod __reexports { - pub use crate::params::ToRpcParams; - pub use crate::params::UnnamedParams; - pub use crate::params::UnnamedParamsBuilder; -} diff --git a/types/src/params.rs b/types/src/params.rs index b89af0b2d2..f5b6583272 100644 --- a/types/src/params.rs +++ b/types/src/params.rs @@ -658,24 +658,6 @@ impl ToRpcParams for () { } } -#[macro_export] -/// Convert the given values to a [`jsonrpsee_types::UnnamedParams`] as expected by a jsonrpsee Client (http or websocket). -macro_rules! rpc_params { - () => { - // ToRpcParams is implemented for the empty tuple. - () - }; - ($($param:expr),*) => { - { - let mut __params = $crate::__reexports::UnnamedParamsBuilder::new(); - $( - __params.insert($param).expect("json serialization is infallible; qed."); - )* - __params.build() - } - }; -} - #[cfg(test)] mod test { use super::{Cow, Id, JsonValue, Params, ParamsSer, SubscriptionId, TwoPointZero}; diff --git a/types/src/request.rs b/types/src/request.rs index a197c0e9e8..03f3f5e81a 100644 --- a/types/src/request.rs +++ b/types/src/request.rs @@ -130,7 +130,7 @@ impl<'a> NotificationSer<'a> { #[cfg(test)] mod test { use super::{Id, InvalidRequest, Notification, NotificationSer, Request, RequestSer, TwoPointZero}; - use crate::{rpc_params, ToRpcParams}; + use crate::{ToRpcParams, UnnamedParamsBuilder}; use serde_json::value::RawValue; fn assert_request<'a>(request: Request<'a>, id: Id<'a>, method: &str, params: Option<&str>) { @@ -207,39 +207,22 @@ mod test { fn serialize_call() { let method = "subtract"; let id = Id::Number(1); // It's enough to check one variant, since the type itself also has tests. - let params = rpc_params![42, 23]; // Same as above. + let mut builder = UnnamedParamsBuilder::new(); + builder.insert(42).unwrap(); + builder.insert(23).unwrap(); + let params = builder.build().to_rpc_params().unwrap(); // Same as above. let test_vector: &[(&'static str, Option<_>, Option<_>, &'static str)] = &[ // With all fields set. - ( - r#"{"jsonrpc":"2.0","id":1,"method":"subtract","params":[42,23]}"#, - Some(&id), - params.clone().to_rpc_params().unwrap(), - method, - ), + (r#"{"jsonrpc":"2.0","id":1,"method":"subtract","params":[42,23]}"#, Some(&id), params.clone(), method), // Escaped method name. - (r#"{"jsonrpc":"2.0","id":1,"method":"\"m"}"#, Some(&id), rpc_params![].to_rpc_params().unwrap(), "\"m"), + (r#"{"jsonrpc":"2.0","id":1,"method":"\"m"}"#, Some(&id), ().to_rpc_params().unwrap(), "\"m"), // Without ID field. - ( - r#"{"jsonrpc":"2.0","id":null,"method":"subtract","params":[42,23]}"#, - None, - params.to_rpc_params().unwrap(), - method, - ), + (r#"{"jsonrpc":"2.0","id":null,"method":"subtract","params":[42,23]}"#, None, params, method), // Without params field - ( - r#"{"jsonrpc":"2.0","id":1,"method":"subtract"}"#, - Some(&id), - rpc_params![].to_rpc_params().unwrap(), - method, - ), + (r#"{"jsonrpc":"2.0","id":1,"method":"subtract"}"#, Some(&id), ().to_rpc_params().unwrap(), method), // Without params and ID. - ( - r#"{"jsonrpc":"2.0","id":null,"method":"subtract"}"#, - None, - rpc_params![].to_rpc_params().unwrap(), - method, - ), + (r#"{"jsonrpc":"2.0","id":null,"method":"subtract"}"#, None, ().to_rpc_params().unwrap(), method), ]; for (ser, id, params, method) in test_vector.iter().cloned() { @@ -258,7 +241,9 @@ mod test { #[test] fn serialize_notif() { let exp = r#"{"jsonrpc":"2.0","method":"say_hello","params":["hello"]}"#; - let params = rpc_params!["hello"].to_rpc_params().unwrap(); + let mut builder = UnnamedParamsBuilder::new(); + builder.insert("hello").unwrap(); + let params = builder.build().to_rpc_params().unwrap(); let req = NotificationSer::new("say_hello", params); let ser = serde_json::to_string(&req).unwrap(); assert_eq!(exp, ser); From 555f53b1dc9f31fcb0072e6c8c3c4859156d9aa5 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 26 Aug 2022 19:19:13 +0300 Subject: [PATCH 10/41] core: Rename server's trait to `ToRpcServerParams` Signed-off-by: Alexandru Vasile --- core/src/server/rpc_module.rs | 6 +++--- core/src/traits.rs | 13 +++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/core/src/server/rpc_module.rs b/core/src/server/rpc_module.rs index d523a4ff25..797dbb4e4e 100644 --- a/core/src/server/rpc_module.rs +++ b/core/src/server/rpc_module.rs @@ -34,7 +34,7 @@ use crate::error::{Error, SubscriptionClosed}; use crate::id_providers::RandomIntegerIdProvider; use crate::server::helpers::{BoundedSubscriptions, MethodSink, SubscriptionPermit}; use crate::server::resource_limiting::{ResourceGuard, ResourceTable, ResourceVec, Resources}; -use crate::traits::{IdProvider, ToRpcParams}; +use crate::traits::{IdProvider, ToRpcServerParams}; use futures_channel::{mpsc, oneshot}; use futures_util::future::Either; use futures_util::pin_mut; @@ -357,7 +357,7 @@ impl Methods { /// assert_eq!(echo, 1); /// } /// ``` - pub async fn call( + pub async fn call( &self, method: &str, params: Params, @@ -473,7 +473,7 @@ impl Methods { /// assert_eq!(&sub_resp, "one answer"); /// } /// ``` - pub async fn subscribe(&self, sub_method: &str, params: impl ToRpcParams) -> Result { + pub async fn subscribe(&self, sub_method: &str, params: impl ToRpcServerParams) -> Result { let params = params.to_rpc_params()?; let req = Request::new(sub_method.into(), Some(¶ms), Id::Number(0)); diff --git a/core/src/traits.rs b/core/src/traits.rs index 3734300ca9..b333edb062 100644 --- a/core/src/traits.rs +++ b/core/src/traits.rs @@ -28,25 +28,26 @@ use jsonrpsee_types::SubscriptionId; use serde::Serialize; use serde_json::value::RawValue; -/// Marker trait for types that can be serialized as JSON array/sequence. +/// Marker trait for types that can be serialized as JSON array/sequence, +/// as part of the serialization done by the server. /// /// If your type isn't a sequence, for example `String`, `usize` or similar /// you must insert it in a tuple, slice, array or Vec for it to work. -pub trait ToRpcParams: Serialize { +pub trait ToRpcServerParams: Serialize { /// Serialize the type as a JSON array. fn to_rpc_params(&self) -> Result, serde_json::Error> { serde_json::to_string(&self).map(|json| RawValue::from_string(json).expect("JSON String; qed")) } } -impl ToRpcParams for &[P] {} -impl ToRpcParams for Vec

{} -impl ToRpcParams for [P; N] where [P; N]: Serialize {} +impl ToRpcServerParams for &[P] {} +impl ToRpcServerParams for Vec

{} +impl ToRpcServerParams for [P; N] where [P; N]: Serialize {} macro_rules! tuple_impls { ($($len:expr => ($($n:tt $name:ident)+))+) => { $( - impl<$($name: Serialize),+> ToRpcParams for ($($name,)+) {} + impl<$($name: Serialize),+> ToRpcServerParams for ($($name,)+) {} )+ } } From 92f9e9e8851dff61e737125b700c019c1a34d631 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Mon, 29 Aug 2022 14:36:43 +0300 Subject: [PATCH 11/41] bench: Adjust benches to the `ToRpcParams` interface Signed-off-by: Alexandru Vasile --- benches/bench.rs | 28 +++++++++++++++++----------- core/src/client/async_client/mod.rs | 6 +++--- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index 7c3fe9dc9b..e28386ee45 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -7,7 +7,7 @@ use futures_util::stream::FuturesUnordered; use helpers::{http_client, ws_client, SUB_METHOD_NAME, UNSUB_METHOD_NAME}; use jsonrpsee::core::client::{ClientT, SubscriptionClientT}; use jsonrpsee::http_client::HeaderMap; -use jsonrpsee::types::{Id, ParamsSer, RequestSer}; +use jsonrpsee::types::{Id, ParamsSer, RequestSer, ToRpcParams, UnnamedParamsBuilder}; use pprof::criterion::{Output, PProfProfiler}; use tokio::runtime::Runtime as TokioRuntime; @@ -67,6 +67,9 @@ pub fn jsonrpsee_types_v2(crit: &mut Criterion) { b.iter(|| { let params = &[1_u64.into(), 2_u32.into()]; let params = ParamsSer::ArrayRef(params); + let params = serde_json::to_string(¶ms).unwrap(); + let params = serde_json::value::RawValue::from_string(params).unwrap(); + let request = RequestSer::new(&Id::Number(0), "say_hello", Some(params)); v2_serialize(request); }) @@ -74,8 +77,11 @@ pub fn jsonrpsee_types_v2(crit: &mut Criterion) { crit.bench_function("jsonrpsee_types_v2_vec", |b| { b.iter(|| { - let params = ParamsSer::Array(vec![1_u64.into(), 2_u32.into()]); - let request = RequestSer::new(&Id::Number(0), "say_hello", Some(params)); + let mut builder = UnnamedParamsBuilder::new(); + builder.insert(1u64).unwrap(); + builder.insert(2u32).unwrap(); + let params = builder.build().to_rpc_params().expect("Valid params"); + let request = RequestSer::new(&Id::Number(0), "say_hello", params); v2_serialize(request); }) }); @@ -129,7 +135,7 @@ fn round_trip(rt: &TokioRuntime, crit: &mut Criterion, client: Arc let bench_name = format!("{}/{}", name, method); crit.bench_function(&request.group_name(&bench_name), |b| { b.to_async(rt).iter(|| async { - black_box(client.request::(method, None).await.unwrap()); + black_box(client.request::(method, ()).await.unwrap()); }) }); } @@ -139,7 +145,7 @@ fn sub_round_trip(rt: &TokioRuntime, crit: &mut Criterion, client: Arc(SUB_METHOD_NAME, None, UNSUB_METHOD_NAME).await.unwrap()); + black_box(client.subscribe::(SUB_METHOD_NAME, (), UNSUB_METHOD_NAME).await.unwrap()); }) }); group.bench_function("subscribe_response", |b| { @@ -149,7 +155,7 @@ fn sub_round_trip(rt: &TokioRuntime, crit: &mut Criterion, client: Arc(SUB_METHOD_NAME, None, UNSUB_METHOD_NAME).await.unwrap() + client.subscribe::(SUB_METHOD_NAME, (), UNSUB_METHOD_NAME).await.unwrap() }) }) }, @@ -166,7 +172,7 @@ fn sub_round_trip(rt: &TokioRuntime, crit: &mut Criterion, client: Arc(SUB_METHOD_NAME, None, UNSUB_METHOD_NAME).await.unwrap() + client.subscribe::(SUB_METHOD_NAME, (), UNSUB_METHOD_NAME).await.unwrap() }) }, |sub| { @@ -227,7 +233,7 @@ fn ws_concurrent_conn_calls(rt: &TokioRuntime, crit: &mut Criterion, url: &str, let futs = FuturesUnordered::new(); for _ in 0..10 { - futs.push(client.request::(methods[0], None)); + futs.push(client.request::(methods[0], ())); } join_all(futs).await; @@ -267,7 +273,7 @@ fn ws_concurrent_conn_subs(rt: &TokioRuntime, crit: &mut Criterion, url: &str, n let futs = FuturesUnordered::new(); for _ in 0..10 { - let fut = client.subscribe::(SUB_METHOD_NAME, None, UNSUB_METHOD_NAME).then( + let fut = client.subscribe::(SUB_METHOD_NAME, (), UNSUB_METHOD_NAME).then( |sub| async move { let mut s = sub.unwrap(); @@ -301,7 +307,7 @@ fn http_concurrent_conn_calls(rt: &TokioRuntime, crit: &mut Criterion, url: &str |clients| async { let tasks = clients.map(|client| { rt.spawn(async move { - client.request::(method, None).await.unwrap(); + client.request::(method, ()).await.unwrap(); }) }); join_all(tasks).await; @@ -333,7 +339,7 @@ fn http_custom_headers_round_trip( crit.bench_function(&request.group_name(&bench_name), |b| { b.to_async(rt).iter(|| async { - black_box(client.request::(method_name, None).await.unwrap()); + black_box(client.request::(method_name, ()).await.unwrap()); }) }); } diff --git a/core/src/client/async_client/mod.rs b/core/src/client/async_client/mod.rs index f96ad4eee7..8b6cd68abb 100644 --- a/core/src/client/async_client/mod.rs +++ b/core/src/client/async_client/mod.rs @@ -156,9 +156,9 @@ impl ClientBuilder { #[cfg(feature = "async-client")] #[cfg_attr(docsrs, doc(cfg(feature = "async-client")))] pub fn build_with_tokio(self, sender: S, receiver: R) -> Client - where - S: TransportSenderT + Send, - R: TransportReceiverT + Send, + where + S: TransportSenderT + Send, + R: TransportReceiverT + Send, { let (to_back, from_front) = mpsc::channel(self.max_concurrent_requests); let (err_tx, err_rx) = oneshot::channel(); From cf941b7eea45f78f060b910a2abda8fc2eb558e8 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Mon, 29 Aug 2022 15:57:15 +0300 Subject: [PATCH 12/41] Fix clippy Signed-off-by: Alexandru Vasile --- proc-macros/src/render_client.rs | 14 ++++---- types/src/params.rs | 60 ++++++++++++++++++-------------- 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/proc-macros/src/render_client.rs b/proc-macros/src/render_client.rs index 2fb36badb2..7bb007ca1e 100644 --- a/proc-macros/src/render_client.rs +++ b/proc-macros/src/render_client.rs @@ -137,7 +137,7 @@ impl RpcDescription { let method = quote! { #docs async fn #rust_method_name(#rust_method_params) -> #returns { - let params = { #parameter_builder }; + let params = #parameter_builder; self.subscribe(#rpc_sub_name, params, #rpc_unsub_name).await } }; @@ -151,7 +151,7 @@ impl RpcDescription { signature: &syn::TraitItemMethod, ) -> TokenStream2 { if params.is_empty() { - return quote! { () }; + return quote!({}); } let jsonrpsee = self.jsonrpsee_client_path.as_ref().unwrap(); @@ -165,22 +165,22 @@ impl RpcDescription { let name = pair.0; // Throw away the type. let (value, _value_type) = pair.1; - quote! { #name, #value } + quote!(#name, #value) }); - quote! { + quote!({ let mut builder = #jsonrpsee::types::NamedParamsBuilder::new(); #( builder.insert( #params_insert ).expect(format!("Parameters {} must be valid", stringify!(#params_insert)).as_str()); )* builder.build() - } + }) } ParamKind::Array => { // Throw away the type. let params = params.iter().map(|(param, _param_type)| param); - quote! { + quote!({ let mut builder = #jsonrpsee::types::UnnamedParamsBuilder::new(); #( builder.insert( #params ).expect(format!("Parameters {} must be valid", stringify!(#params)).as_str()); )* builder.build() - } + }) } } } diff --git a/types/src/params.rs b/types/src/params.rs index f5b6583272..cac5d280f6 100644 --- a/types/src/params.rs +++ b/types/src/params.rs @@ -446,9 +446,9 @@ impl ParamsBuilder { /// The _name_ and _value_ are delimited by the `:` token. pub fn insert_named(&mut self, name: &str, value: P) -> Result<(), serde_json::Error> { serde_json::to_writer(&mut self.bytes, name)?; - self.bytes.push(':' as u8); + self.bytes.push(b':'); serde_json::to_writer(&mut self.bytes, &value)?; - self.bytes.push(',' as u8); + self.bytes.push(b','); Ok(()) } @@ -456,7 +456,7 @@ impl ParamsBuilder { /// Insert a plain value into the builder. pub fn insert(&mut self, value: P) -> Result<(), serde_json::Error> { serde_json::to_writer(&mut self.bytes, &value)?; - self.bytes.push(',' as u8); + self.bytes.push(b','); Ok(()) } @@ -464,7 +464,7 @@ impl ParamsBuilder { /// Finish the building process and return a JSON compatible string. pub fn build(mut self) -> String { let idx = self.bytes.len() - 1; - if self.bytes[idx] == ',' as u8 { + if self.bytes[idx] == b',' { self.bytes[idx] = self.end as u8; } else { self.bytes.push(self.end as u8); @@ -484,14 +484,12 @@ impl ParamsBuilder { /// /// use jsonrpsee_types::NamedParamsBuilder; /// -/// fn main() { -/// let mut builder = NamedParamsBuilder::new(); -/// builder.insert("param1", 1); -/// builder.insert("param2", "abc"); -/// let params = builder.build(); +/// let mut builder = NamedParamsBuilder::new(); +/// builder.insert("param1", 1); +/// builder.insert("param2", "abc"); +/// let params = builder.build(); /// -/// // Use RPC parameters... -/// } +/// // Use RPC parameters... /// ``` #[derive(Debug)] pub struct NamedParamsBuilder(ParamsBuilder); @@ -499,7 +497,7 @@ pub struct NamedParamsBuilder(ParamsBuilder); impl NamedParamsBuilder { /// Construct a new [`NamedParamsBuilder`]. pub fn new() -> Self { - Self(ParamsBuilder::new('{', '}')) + Self::default() } /// Insert a named value (key, value) pair into the builder. @@ -514,13 +512,19 @@ impl NamedParamsBuilder { } } +impl Default for NamedParamsBuilder { + fn default() -> Self { + Self(ParamsBuilder::new('{', '}')) + } +} + /// Named RPC parameters stored as a JSON Map object `{ key: value }`. #[derive(Clone, Debug)] pub struct NamedParams(String); impl ToRpcParams for NamedParams { fn to_rpc_params(self) -> Result>, serde_json::Error> { - RawValue::from_string(self.0).map(|res| Some(res)) + RawValue::from_string(self.0).map(Some) } } @@ -533,14 +537,12 @@ impl ToRpcParams for NamedParams { /// /// use jsonrpsee_types::UnnamedParamsBuilder; /// -/// fn main() { -/// let mut builder = UnnamedParamsBuilder::new(); -/// builder.insert("param1"); -/// builder.insert(1); -/// let params = builder.build(); +/// let mut builder = UnnamedParamsBuilder::new(); +/// builder.insert("param1"); +/// builder.insert(1); +/// let params = builder.build(); /// -/// // Use RPC parameters... -/// } +/// // Use RPC parameters... /// ``` #[derive(Debug)] pub struct UnnamedParamsBuilder(ParamsBuilder); @@ -548,7 +550,7 @@ pub struct UnnamedParamsBuilder(ParamsBuilder); impl UnnamedParamsBuilder { /// Construct a new [`UnnamedParamsBuilder`]. pub fn new() -> Self { - Self(ParamsBuilder::new('[', ']')) + Self::default() } /// Insert a plain value into the builder. @@ -562,13 +564,19 @@ impl UnnamedParamsBuilder { } } +impl Default for UnnamedParamsBuilder { + fn default() -> Self { + Self(ParamsBuilder::new('[', ']')) + } +} + /// Unnamed RPC parameters stored as a JSON Array object `[ value0, value1, .., valueN ]`. #[derive(Clone, Debug)] pub struct UnnamedParams(String); impl ToRpcParams for UnnamedParams { fn to_rpc_params(self) -> Result>, serde_json::Error> { - RawValue::from_string(self.0).map(|res| Some(res)) + RawValue::from_string(self.0).map(Some) } } @@ -595,7 +603,7 @@ impl ToRpcParams for UnnamedParams { /// impl ToRpcParams for ManualParam { /// fn to_rpc_params(self) -> Result>, serde_json::Error> { /// // Manually define a valid JSONRPC parameter. -/// RawValue::from_string("[1, \"2\", 3]".to_string()).map(|res| Some(res)) +/// RawValue::from_string("[1, \"2\", 3]".to_string()).map(Some) /// } /// } /// ``` @@ -615,8 +623,8 @@ impl ToRpcParams for UnnamedParams { /// /// impl ToRpcParams for SerParam { /// fn to_rpc_params(self) -> Result>, serde_json::Error> { -/// let s = String::from_utf8(serde_json::to_vec(&self)?).expect("Valid UTF8 format"); -/// RawValue::from_string(s).map(|res| Some(res)) +/// let s = String::from_utf8(serde_json::to_vec(&self)?).expect("Valid UTF8 format"); +/// RawValue::from_string(s).map(Some) /// } /// } /// ``` @@ -630,7 +638,7 @@ const BATCH_PARAMS_NUM_CAPACITY: usize = 4; /// Parameter builder that serializes RPC parameters to construct a valid batch parameter. /// This is the equivalent of chaining multiple RPC requests. -#[derive(Debug)] +#[derive(Debug, Default)] pub struct BatchParamsBuilder<'a>(Vec<(&'a str, Option>)>); impl<'a> BatchParamsBuilder<'a> { From c48cc4a3d1eef519d66c55cbee59cf1db6dae5f3 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Mon, 29 Aug 2022 16:58:20 +0300 Subject: [PATCH 13/41] types: Rename batch builder to `BatchRequestBuilder` Signed-off-by: Alexandru Vasile --- client/http-client/src/tests.rs | 6 +++--- client/ws-client/src/tests.rs | 6 +++--- core/src/client/mod.rs | 2 +- tests/tests/integration_tests.rs | 4 ++-- types/src/lib.rs | 2 +- types/src/params.rs | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/client/http-client/src/tests.rs b/client/http-client/src/tests.rs index c38a857d88..084d67b64f 100644 --- a/client/http-client/src/tests.rs +++ b/client/http-client/src/tests.rs @@ -34,7 +34,7 @@ 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::BatchParamsBuilder; +use jsonrpsee_types::BatchRequestBuilder; use serde_json::value::RawValue; #[tokio::test] @@ -137,7 +137,7 @@ async fn subscription_response_to_request() { #[tokio::test] async fn batch_request_works() { - let mut builder = BatchParamsBuilder::new(); + let mut builder = BatchRequestBuilder::new(); builder.insert("say_hello", rpc_params![]).unwrap(); builder.insert("say_goodbye", rpc_params![0_u64, 1, 2]).unwrap(); builder.insert("get_swag", rpc_params![]).unwrap(); @@ -150,7 +150,7 @@ async fn batch_request_works() { #[tokio::test] async fn batch_request_out_of_order_response() { - let mut builder = BatchParamsBuilder::new(); + let mut builder = BatchRequestBuilder::new(); builder.insert("say_hello", rpc_params![]).unwrap(); builder.insert("say_goodbye", rpc_params![0_u64, 1, 2]).unwrap(); builder.insert("get_swag", rpc_params![]).unwrap(); diff --git a/client/ws-client/src/tests.rs b/client/ws-client/src/tests.rs index ba89146a9a..2e62ac4111 100644 --- a/client/ws-client/src/tests.rs +++ b/client/ws-client/src/tests.rs @@ -35,7 +35,7 @@ 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::BatchParamsBuilder; +use jsonrpsee_types::BatchRequestBuilder; use serde_json::value::RawValue; use serde_json::Value as JsonValue; @@ -227,7 +227,7 @@ async fn notification_without_polling_doesnt_make_client_unuseable() { #[tokio::test] async fn batch_request_works() { - let mut builder = BatchParamsBuilder::new(); + let mut builder = BatchRequestBuilder::new(); builder.insert("say_hello", rpc_params![]).unwrap(); builder.insert("say_goodbye", rpc_params![0_u64, 1, 2]).unwrap(); builder.insert("get_swag", rpc_params![]).unwrap(); @@ -240,7 +240,7 @@ async fn batch_request_works() { #[tokio::test] async fn batch_request_out_of_order_response() { - let mut builder = BatchParamsBuilder::new(); + let mut builder = BatchRequestBuilder::new(); builder.insert("say_hello", rpc_params![]).unwrap(); builder.insert("say_goodbye", rpc_params![0_u64, 1, 2]).unwrap(); builder.insert("get_swag", rpc_params![]).unwrap(); diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index 452518ff67..52f2065da7 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -192,7 +192,7 @@ macro_rules! rpc_params { { let mut __params = $crate::client::__reexports::UnnamedParamsBuilder::new(); $( - __params.insert($param).expect("json serialization is infallible; qed."); + __params.insert($param).expect(format!("Parameter `{}` cannot be serialized", stringify!($param)).as_str()); )* __params.build() } diff --git a/tests/tests/integration_tests.rs b/tests/tests/integration_tests.rs index bfa214edde..68dfbdc1d3 100644 --- a/tests/tests/integration_tests.rs +++ b/tests/tests/integration_tests.rs @@ -40,7 +40,7 @@ use jsonrpsee::http_client::HttpClientBuilder; use jsonrpsee::http_server::AccessControlBuilder; use jsonrpsee::rpc_params; use jsonrpsee::types::error::ErrorObject; -use jsonrpsee::types::{BatchParamsBuilder, UnnamedParams}; +use jsonrpsee::types::{BatchRequestBuilder, UnnamedParams}; use jsonrpsee::ws_client::WsClientBuilder; use tokio::time::interval; use tokio_stream::wrappers::IntervalStream; @@ -659,7 +659,7 @@ async fn ws_batch_works() { let server_url = format!("ws://{}", server_addr); let client = WsClientBuilder::default().build(&server_url).await.unwrap(); - let mut batch = BatchParamsBuilder::new(); + let mut batch = BatchRequestBuilder::new(); batch.insert("say_hello", rpc_params![]).unwrap(); batch.insert("slow_hello", rpc_params![]).unwrap(); diff --git a/types/src/lib.rs b/types/src/lib.rs index 6449f9b57f..ee1318ac25 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -44,7 +44,7 @@ pub mod error; pub use error::{ErrorObject, ErrorObjectOwned, ErrorResponse, SubscriptionEmptyError, SubscriptionResult}; pub use params::{ - BatchParamsBuilder, Id, NamedParams, NamedParamsBuilder, Params, ParamsSequence, ParamsSer, SubscriptionId, + BatchRequestBuilder, Id, NamedParams, NamedParamsBuilder, Params, ParamsSequence, ParamsSer, SubscriptionId, ToRpcParams, TwoPointZero, UnnamedParams, UnnamedParamsBuilder, }; pub use request::{InvalidRequest, Notification, NotificationSer, Request, RequestSer}; diff --git a/types/src/params.rs b/types/src/params.rs index cac5d280f6..ae26614299 100644 --- a/types/src/params.rs +++ b/types/src/params.rs @@ -639,9 +639,9 @@ const BATCH_PARAMS_NUM_CAPACITY: usize = 4; /// Parameter builder that serializes RPC parameters to construct a valid batch parameter. /// This is the equivalent of chaining multiple RPC requests. #[derive(Debug, Default)] -pub struct BatchParamsBuilder<'a>(Vec<(&'a str, Option>)>); +pub struct BatchRequestBuilder<'a>(Vec<(&'a str, Option>)>); -impl<'a> BatchParamsBuilder<'a> { +impl<'a> BatchRequestBuilder<'a> { /// Construct a new [`BatchParamsBuilder`]. pub fn new() -> Self { Self(Vec::with_capacity(BATCH_PARAMS_NUM_CAPACITY)) From 89b03c586c4ec50b69061f88dfa2173acf0f44db Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Mon, 29 Aug 2022 16:59:10 +0300 Subject: [PATCH 14/41] examples: Re-enable proc-macro example Signed-off-by: Alexandru Vasile --- examples/examples/proc_macro.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/examples/proc_macro.rs b/examples/examples/proc_macro.rs index e0b0aa8abc..05bedba2b5 100644 --- a/examples/examples/proc_macro.rs +++ b/examples/examples/proc_macro.rs @@ -7,11 +7,11 @@ pub trait Rpc { #[method(name = "foo")] async fn async_method(&self, param_a: u8, param_b: String) -> RpcResult; - // #[method(name = "bar")] - // fn sync_method(&self) -> RpcResult; + #[method(name = "bar")] + fn sync_method(&self) -> RpcResult; - // #[subscription(name = "subscribe", item = String)] - // fn sub(&self); + #[subscription(name = "subscribe", item = String)] + fn sub(&self); } fn main() {} From 07a888f82b219ee03021dc6e082a90713adf1a6c Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Mon, 29 Aug 2022 17:13:37 +0300 Subject: [PATCH 15/41] types: Fix doc tests and add panic documentation Signed-off-by: Alexandru Vasile --- core/src/client/mod.rs | 7 ++++++- types/src/params.rs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index 52f2065da7..1682e19189 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -181,8 +181,13 @@ pub trait TransportReceiverT: 'static { async fn receive(&mut self) -> Result; } +/// Convert the given values to a [`jsonrpsee_types::UnnamedParams`], or empty tuple as expected by a +/// jsonrpsee Client (http or websocket). +/// +/// # Panics +/// +/// Panics if the serialization of parameters fails. #[macro_export] -/// Convert the given values to a [`jsonrpsee_types::UnnamedParams`] as expected by a jsonrpsee Client (http or websocket). macro_rules! rpc_params { () => { // ToRpcParams is implemented for the empty tuple. diff --git a/types/src/params.rs b/types/src/params.rs index ae26614299..00b7f4559a 100644 --- a/types/src/params.rs +++ b/types/src/params.rs @@ -642,7 +642,7 @@ const BATCH_PARAMS_NUM_CAPACITY: usize = 4; pub struct BatchRequestBuilder<'a>(Vec<(&'a str, Option>)>); impl<'a> BatchRequestBuilder<'a> { - /// Construct a new [`BatchParamsBuilder`]. + /// Construct a new [`BatchRequestBuilder`]. pub fn new() -> Self { Self(Vec::with_capacity(BATCH_PARAMS_NUM_CAPACITY)) } From faf016cf2d33adcf054ce5743a5ad6c94bc923c2 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Mon, 29 Aug 2022 17:41:26 +0300 Subject: [PATCH 16/41] core: Fix documentation link Signed-off-by: Alexandru Vasile --- core/src/server/rpc_module.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/server/rpc_module.rs b/core/src/server/rpc_module.rs index 797dbb4e4e..8811a27270 100644 --- a/core/src/server/rpc_module.rs +++ b/core/src/server/rpc_module.rs @@ -337,7 +337,7 @@ impl Methods { /// Helper to call a method on the `RPC module` without having to spin up a server. /// - /// The params must be serializable as JSON array, see [`ToRpcParams`] for further documentation. + /// The params must be serializable as JSON array, see [`jsonrpsee_types::ToRpcParams`] for further documentation. /// /// Returns the decoded value of the `result field` in JSON-RPC response if successful. /// @@ -450,7 +450,7 @@ impl Methods { /// Helper to create a subscription on the `RPC module` without having to spin up a server. /// - /// The params must be serializable as JSON array, see [`ToRpcParams`] for further documentation. + /// The params must be serializable as JSON array, see [`jsonrpsee_types::ToRpcParams`] for further documentation. /// /// Returns [`Subscription`] on success which can used to get results from the subscriptions. /// From 7485bbc2cc4aafe2519037ef3e251822c4484a20 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Tue, 30 Aug 2022 14:18:29 +0300 Subject: [PATCH 17/41] client: Use BatchRequestBuilder as parameter for batch requests Signed-off-by: Alexandru Vasile --- benches/bench.rs | 7 +++++-- client/http-client/src/client.rs | 5 +++-- client/http-client/src/tests.rs | 22 ++++++++++------------ client/ws-client/src/tests.rs | 22 ++++++++++------------ core/src/client/async_client/mod.rs | 6 +++--- core/src/client/mod.rs | 5 ++--- tests/tests/integration_tests.rs | 2 +- types/src/params.rs | 4 ++-- 8 files changed, 36 insertions(+), 37 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index e28386ee45..4f51277524 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -7,7 +7,7 @@ use futures_util::stream::FuturesUnordered; use helpers::{http_client, ws_client, SUB_METHOD_NAME, UNSUB_METHOD_NAME}; use jsonrpsee::core::client::{ClientT, SubscriptionClientT}; use jsonrpsee::http_client::HeaderMap; -use jsonrpsee::types::{Id, ParamsSer, RequestSer, ToRpcParams, UnnamedParamsBuilder}; +use jsonrpsee::types::{BatchRequestBuilder, Id, ParamsSer, RequestSer, ToRpcParams, UnnamedParamsBuilder}; use pprof::criterion::{Output, PProfProfiler}; use tokio::runtime::Runtime as TokioRuntime; @@ -197,7 +197,10 @@ fn batch_round_trip( let bench_name = format!("{}/{}", name, method); let mut group = crit.benchmark_group(request.group_name(&bench_name)); for batch_size in [2, 5, 10, 50, 100usize].iter() { - let batch = vec![(method, None); *batch_size]; + let mut batch = BatchRequestBuilder::new(); + for _ in 0..*batch_size { + batch.insert(method, ()).unwrap(); + } group.throughput(Throughput::Elements(*batch_size as u64)); group.bench_with_input(BenchmarkId::from_parameter(batch_size), batch_size, |b, _| { b.to_async(rt).iter(|| async { client.batch_request::(batch.clone()).await.unwrap() }) diff --git a/client/http-client/src/client.rs b/client/http-client/src/client.rs index 078f2cc4bf..fdb6397209 100644 --- a/client/http-client/src/client.rs +++ b/client/http-client/src/client.rs @@ -36,9 +36,9 @@ use jsonrpsee_core::tracing::RpcTracing; use jsonrpsee_core::{Error, TEN_MB_SIZE_BYTES}; use jsonrpsee_types::error::CallError; use jsonrpsee_types::params::ToRpcParams; +use jsonrpsee_types::BatchRequestBuilder; use rustc_hash::FxHashMap; use serde::de::DeserializeOwned; -use serde_json::value::RawValue; use tracing_futures::Instrument; /// Http Client Builder. @@ -234,10 +234,11 @@ impl ClientT for HttpClient { .await } - async fn batch_request(&self, batch: Vec<(&str, Option>)>) -> Result, Error> + async fn batch_request<'a, R>(&self, batch: BatchRequestBuilder<'a>) -> Result, Error> where R: DeserializeOwned + Default + Clone, { + let batch = batch.build(); let guard = self.id_manager.next_request_ids(batch.len())?; let ids: Vec = guard.inner(); let trace = RpcTracing::batch(); diff --git a/client/http-client/src/tests.rs b/client/http-client/src/tests.rs index 084d67b64f..37cd8a8194 100644 --- a/client/http-client/src/tests.rs +++ b/client/http-client/src/tests.rs @@ -137,11 +137,10 @@ async fn subscription_response_to_request() { #[tokio::test] async fn batch_request_works() { - let mut builder = BatchRequestBuilder::new(); - builder.insert("say_hello", rpc_params![]).unwrap(); - builder.insert("say_goodbye", rpc_params![0_u64, 1, 2]).unwrap(); - builder.insert("get_swag", rpc_params![]).unwrap(); - let batch_request = builder.build(); + let mut batch_request = BatchRequestBuilder::new(); + batch_request.insert("say_hello", rpc_params![]).unwrap(); + batch_request.insert("say_goodbye", rpc_params![0_u64, 1, 2]).unwrap(); + batch_request.insert("get_swag", rpc_params![]).unwrap(); let server_response = r#"[{"jsonrpc":"2.0","result":"hello","id":0}, {"jsonrpc":"2.0","result":"goodbye","id":1}, {"jsonrpc":"2.0","result":"here's your swag","id":2}]"#.to_string(); let response = run_batch_request_with_response(batch_request, server_response).with_default_timeout().await.unwrap().unwrap(); @@ -150,19 +149,18 @@ async fn batch_request_works() { #[tokio::test] async fn batch_request_out_of_order_response() { - let mut builder = BatchRequestBuilder::new(); - builder.insert("say_hello", rpc_params![]).unwrap(); - builder.insert("say_goodbye", rpc_params![0_u64, 1, 2]).unwrap(); - builder.insert("get_swag", rpc_params![]).unwrap(); - let batch_request = builder.build(); + let mut batch_request = BatchRequestBuilder::new(); + batch_request.insert("say_hello", rpc_params![]).unwrap(); + batch_request.insert("say_goodbye", rpc_params![0_u64, 1, 2]).unwrap(); + batch_request.insert("get_swag", rpc_params![]).unwrap(); let server_response = r#"[{"jsonrpc":"2.0","result":"here's your swag","id":2}, {"jsonrpc":"2.0","result":"hello","id":0}, {"jsonrpc":"2.0","result":"goodbye","id":1}]"#.to_string(); let response = run_batch_request_with_response(batch_request, server_response).with_default_timeout().await.unwrap().unwrap(); assert_eq!(response, vec!["hello".to_string(), "goodbye".to_string(), "here's your swag".to_string()]); } -async fn run_batch_request_with_response( - batch: Vec<(&str, Option>)>, +async fn run_batch_request_with_response<'a>( + batch: BatchRequestBuilder<'a>, response: String, ) -> Result, Error> { let server_addr = http_server_with_hardcoded_response(response).with_default_timeout().await.unwrap(); diff --git a/client/ws-client/src/tests.rs b/client/ws-client/src/tests.rs index 2e62ac4111..f86d23856b 100644 --- a/client/ws-client/src/tests.rs +++ b/client/ws-client/src/tests.rs @@ -227,11 +227,10 @@ async fn notification_without_polling_doesnt_make_client_unuseable() { #[tokio::test] async fn batch_request_works() { - let mut builder = BatchRequestBuilder::new(); - builder.insert("say_hello", rpc_params![]).unwrap(); - builder.insert("say_goodbye", rpc_params![0_u64, 1, 2]).unwrap(); - builder.insert("get_swag", rpc_params![]).unwrap(); - let batch_request = builder.build(); + let mut batch_request = BatchRequestBuilder::new(); + batch_request.insert("say_hello", rpc_params![]).unwrap(); + batch_request.insert("say_goodbye", rpc_params![0_u64, 1, 2]).unwrap(); + batch_request.insert("get_swag", rpc_params![]).unwrap(); let server_response = r#"[{"jsonrpc":"2.0","result":"hello","id":0}, {"jsonrpc":"2.0","result":"goodbye","id":1}, {"jsonrpc":"2.0","result":"here's your swag","id":2}]"#.to_string(); let response = run_batch_request_with_response(batch_request, server_response).with_default_timeout().await.unwrap().unwrap(); @@ -240,11 +239,10 @@ async fn batch_request_works() { #[tokio::test] async fn batch_request_out_of_order_response() { - let mut builder = BatchRequestBuilder::new(); - builder.insert("say_hello", rpc_params![]).unwrap(); - builder.insert("say_goodbye", rpc_params![0_u64, 1, 2]).unwrap(); - builder.insert("get_swag", rpc_params![]).unwrap(); - let batch_request = builder.build(); + let mut batch_request = BatchRequestBuilder::new(); + batch_request.insert("say_hello", rpc_params![]).unwrap(); + batch_request.insert("say_goodbye", rpc_params![0_u64, 1, 2]).unwrap(); + batch_request.insert("get_swag", rpc_params![]).unwrap(); let server_response = r#"[{"jsonrpc":"2.0","result":"here's your swag","id":2}, {"jsonrpc":"2.0","result":"hello","id":0}, {"jsonrpc":"2.0","result":"goodbye","id":1}]"#.to_string(); let response = run_batch_request_with_response(batch_request, server_response).with_default_timeout().await.unwrap().unwrap(); @@ -277,8 +275,8 @@ async fn is_connected_works() { assert!(!client.is_connected()) } -async fn run_batch_request_with_response( - batch: Vec<(&str, Option>)>, +async fn run_batch_request_with_response<'a>( + batch: BatchRequestBuilder<'a>, response: String, ) -> Result, Error> { let server = WebSocketTestServer::with_hardcoded_response("127.0.0.1:0".parse().unwrap(), response) diff --git a/core/src/client/async_client/mod.rs b/core/src/client/async_client/mod.rs index 8b6cd68abb..9e060d0087 100644 --- a/core/src/client/async_client/mod.rs +++ b/core/src/client/async_client/mod.rs @@ -28,10 +28,9 @@ use futures_util::stream::StreamExt; use futures_util::FutureExt; use jsonrpsee_types::{ response::SubscriptionError, ErrorResponse, Id, Notification, NotificationSer, RequestSer, Response, - SubscriptionResponse, + SubscriptionResponse, BatchRequestBuilder }; use serde::de::DeserializeOwned; -use serde_json::value::RawValue; use tracing_futures::Instrument; use jsonrpsee_types::params::ToRpcParams; @@ -342,12 +341,13 @@ impl ClientT for Client .await } - async fn batch_request(&self, batch: Vec<(&str, Option>)>) -> Result, Error> + async fn batch_request<'a, R>(&self, batch: BatchRequestBuilder<'a>) -> Result, Error> where R: DeserializeOwned + Default + Clone { let trace = RpcTracing::batch(); async { + let batch = batch.build(); let guard = self.id_manager.next_request_ids(batch.len())?; let batch_ids: Vec = guard.inner(); let mut batches = Vec::with_capacity(batch.len()); diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index 1682e19189..a911bdfba7 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -38,10 +38,9 @@ use futures_channel::{mpsc, oneshot}; use futures_util::future::FutureExt; use futures_util::sink::SinkExt; use futures_util::stream::{Stream, StreamExt}; -use jsonrpsee_types::{Id, SubscriptionId}; +use jsonrpsee_types::{BatchRequestBuilder, Id, SubscriptionId}; use serde::de::DeserializeOwned; use serde_json::Value as JsonValue; -use serde_json::value::RawValue; use jsonrpsee_types::params::ToRpcParams; // Re-exports for the `rpc_params` macro. @@ -78,7 +77,7 @@ pub trait ClientT { /// /// Returns `Ok` if all requests in the batch were answered successfully. /// Returns `Error` if any of the requests in batch fails. - async fn batch_request(&self, batch: Vec<(&str, Option>)>) -> Result, Error> + async fn batch_request<'a, R>(&self, batch: BatchRequestBuilder<'a>) -> Result, Error> where R: DeserializeOwned + Default + Clone; } diff --git a/tests/tests/integration_tests.rs b/tests/tests/integration_tests.rs index 68dfbdc1d3..5ee09a0b54 100644 --- a/tests/tests/integration_tests.rs +++ b/tests/tests/integration_tests.rs @@ -663,7 +663,7 @@ async fn ws_batch_works() { batch.insert("say_hello", rpc_params![]).unwrap(); batch.insert("slow_hello", rpc_params![]).unwrap(); - let responses: Vec = client.batch_request(batch.build()).await.unwrap(); + let responses: Vec = client.batch_request(batch).await.unwrap(); assert_eq!(responses, vec!["hello".to_string(), "hello".to_string()]); } diff --git a/types/src/params.rs b/types/src/params.rs index 00b7f4559a..f932c65ad7 100644 --- a/types/src/params.rs +++ b/types/src/params.rs @@ -636,9 +636,9 @@ pub trait ToRpcParams { /// Initial number of parameters in a batch request. const BATCH_PARAMS_NUM_CAPACITY: usize = 4; -/// Parameter builder that serializes RPC parameters to construct a valid batch parameter. +/// Request builder that serializes RPC parameters to construct a valid batch parameter. /// This is the equivalent of chaining multiple RPC requests. -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct BatchRequestBuilder<'a>(Vec<(&'a str, Option>)>); impl<'a> BatchRequestBuilder<'a> { From 0e3e58ede93780da56dbfcdf71bb14daceae867b Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Wed, 31 Aug 2022 11:08:05 +0300 Subject: [PATCH 18/41] Update core/src/server/rpc_module.rs Co-authored-by: Niklas Adolfsson --- core/src/server/rpc_module.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/server/rpc_module.rs b/core/src/server/rpc_module.rs index 8811a27270..a1f361f55f 100644 --- a/core/src/server/rpc_module.rs +++ b/core/src/server/rpc_module.rs @@ -337,7 +337,7 @@ impl Methods { /// Helper to call a method on the `RPC module` without having to spin up a server. /// - /// The params must be serializable as JSON array, see [`jsonrpsee_types::ToRpcParams`] for further documentation. + /// The params must be serializable as JSON array, see [`ToRpcServerParams`] for further documentation. /// /// Returns the decoded value of the `result field` in JSON-RPC response if successful. /// From ee213d5dade4119cbb907dc12a5aa4d8c78e6cbb Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Wed, 31 Aug 2022 11:09:24 +0300 Subject: [PATCH 19/41] Update core/src/server/rpc_module.rs Co-authored-by: Niklas Adolfsson --- core/src/server/rpc_module.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/server/rpc_module.rs b/core/src/server/rpc_module.rs index a1f361f55f..19e903ba06 100644 --- a/core/src/server/rpc_module.rs +++ b/core/src/server/rpc_module.rs @@ -450,7 +450,7 @@ impl Methods { /// Helper to create a subscription on the `RPC module` without having to spin up a server. /// - /// The params must be serializable as JSON array, see [`jsonrpsee_types::ToRpcParams`] for further documentation. + /// The params must be serializable as JSON array, see [`ToRpcServerParams`] for further documentation. /// /// Returns [`Subscription`] on success which can used to get results from the subscriptions. /// From b460c942ca5313b811273467878c888602962d17 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 31 Aug 2022 11:24:39 +0300 Subject: [PATCH 20/41] types: Add specialized constructors for internal `ParamsBuilder` Signed-off-by: Alexandru Vasile --- types/src/params.rs | 117 +++++++++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 51 deletions(-) diff --git a/types/src/params.rs b/types/src/params.rs index f932c65ad7..0f1f79cb32 100644 --- a/types/src/params.rs +++ b/types/src/params.rs @@ -416,62 +416,77 @@ impl<'a> Id<'a> { } } -/// Initial number of bytes for a parameter length. -const PARAM_BYTES_CAPACITY: usize = 128; +/// Helper module for building parameters. +mod params_builder { + use serde::Serialize; -/// Generic parameter builder that serializes parameters to bytes. -/// This produces a JSON compatible String. -/// -/// The implementation relies on `Vec` to hold the serialized -/// parameters in memory for the following reasons: -/// 1. Other serialization methods than `serde_json::to_writer` would internally -/// have an extra heap allocation for temporarily holding the value in memory. -/// 2. `io::Write` is not implemented for `String` required for serialization. -#[derive(Debug)] -struct ParamsBuilder { - bytes: Vec, - end: char, -} + /// Initial number of bytes for a parameter length. + const PARAM_BYTES_CAPACITY: usize = 128; -impl ParamsBuilder { - /// Construct a new [`ParamsBuilder`] with custom start and end tokens. - /// The inserted values are wrapped by the _start_ and _end_ tokens. - fn new(start: char, end: char) -> Self { - let mut bytes = Vec::with_capacity(PARAM_BYTES_CAPACITY); - bytes.push(start as u8); - ParamsBuilder { bytes, end } - } + /// Generic parameter builder that serializes parameters to bytes. + /// This produces a JSON compatible String. + /// + /// The implementation relies on `Vec` to hold the serialized + /// parameters in memory for the following reasons: + /// 1. Other serialization methods than `serde_json::to_writer` would internally + /// have an extra heap allocation for temporarily holding the value in memory. + /// 2. `io::Write` is not implemented for `String` required for serialization. + #[derive(Debug)] + pub struct ParamsBuilder { + bytes: Vec, + end: char, + } + + impl ParamsBuilder { + /// Construct a new [`ParamsBuilder`] with custom start and end tokens. + /// The inserted values are wrapped by the _start_ and _end_ tokens. + fn new(start: char, end: char) -> Self { + let mut bytes = Vec::with_capacity(PARAM_BYTES_CAPACITY); + bytes.push(start as u8); + ParamsBuilder { bytes, end } + } - /// Insert a named value (key, value) pair into the builder. - /// The _name_ and _value_ are delimited by the `:` token. - pub fn insert_named(&mut self, name: &str, value: P) -> Result<(), serde_json::Error> { - serde_json::to_writer(&mut self.bytes, name)?; - self.bytes.push(b':'); - serde_json::to_writer(&mut self.bytes, &value)?; - self.bytes.push(b','); + /// Construct a new [`ParamsBuilder`] for positional parameters equivalent to a JSON array object. + pub fn positional() -> Self { + Self::new('[', ']') + } - Ok(()) - } + /// Construct a new [`ParamsBuilder`] for named parameters equivalent to a JSON map object. + pub fn named() -> Self { + Self::new('{', '}') + } - /// Insert a plain value into the builder. - pub fn insert(&mut self, value: P) -> Result<(), serde_json::Error> { - serde_json::to_writer(&mut self.bytes, &value)?; - self.bytes.push(b','); + /// Insert a named value (key, value) pair into the builder. + /// The _name_ and _value_ are delimited by the `:` token. + pub fn insert_named(&mut self, name: &str, value: P) -> Result<(), serde_json::Error> { + serde_json::to_writer(&mut self.bytes, name)?; + self.bytes.push(b':'); + serde_json::to_writer(&mut self.bytes, &value)?; + self.bytes.push(b','); - Ok(()) - } + Ok(()) + } - /// Finish the building process and return a JSON compatible string. - pub fn build(mut self) -> String { - let idx = self.bytes.len() - 1; - if self.bytes[idx] == b',' { - self.bytes[idx] = self.end as u8; - } else { - self.bytes.push(self.end as u8); + /// Insert a plain value into the builder. + pub fn insert(&mut self, value: P) -> Result<(), serde_json::Error> { + serde_json::to_writer(&mut self.bytes, &value)?; + self.bytes.push(b','); + + Ok(()) } - // Safety: This is safe because we do not emit invalid UTF-8. - unsafe { String::from_utf8_unchecked(self.bytes) } + /// Finish the building process and return a JSON compatible string. + pub fn build(mut self) -> String { + let idx = self.bytes.len() - 1; + if self.bytes[idx] == b',' { + self.bytes[idx] = self.end as u8; + } else { + self.bytes.push(self.end as u8); + } + + // Safety: This is safe because we do not emit invalid UTF-8. + unsafe { String::from_utf8_unchecked(self.bytes) } + } } } @@ -492,7 +507,7 @@ impl ParamsBuilder { /// // Use RPC parameters... /// ``` #[derive(Debug)] -pub struct NamedParamsBuilder(ParamsBuilder); +pub struct NamedParamsBuilder(params_builder::ParamsBuilder); impl NamedParamsBuilder { /// Construct a new [`NamedParamsBuilder`]. @@ -514,7 +529,7 @@ impl NamedParamsBuilder { impl Default for NamedParamsBuilder { fn default() -> Self { - Self(ParamsBuilder::new('{', '}')) + Self(params_builder::ParamsBuilder::named()) } } @@ -545,7 +560,7 @@ impl ToRpcParams for NamedParams { /// // Use RPC parameters... /// ``` #[derive(Debug)] -pub struct UnnamedParamsBuilder(ParamsBuilder); +pub struct UnnamedParamsBuilder(params_builder::ParamsBuilder); impl UnnamedParamsBuilder { /// Construct a new [`UnnamedParamsBuilder`]. @@ -566,7 +581,7 @@ impl UnnamedParamsBuilder { impl Default for UnnamedParamsBuilder { fn default() -> Self { - Self(ParamsBuilder::new('[', ']')) + Self(params_builder::ParamsBuilder::positional()) } } From 706a67233426e7ed5072fdc69d06e01355a4960b Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 31 Aug 2022 11:42:49 +0300 Subject: [PATCH 21/41] types: Implement `EmptyParams` for client's parameters Signed-off-by: Alexandru Vasile --- benches/bench.rs | 33 ++++++++++------ client/http-client/src/tests.rs | 1 - client/ws-client/src/tests.rs | 1 - core/src/client/mod.rs | 5 ++- core/src/server/rpc_module.rs | 4 +- proc-macros/src/render_client.rs | 8 ++-- proc-macros/tests/ui/correct/basic.rs | 4 +- tests/tests/integration_tests.rs | 56 ++++++++++++++++++--------- tests/tests/resource_limiting.rs | 38 +++++++++--------- tests/tests/rpc_module.rs | 28 +++++++------- types/src/lib.rs | 8 ++-- types/src/params.rs | 8 +++- types/src/request.rs | 13 +++++-- 13 files changed, 123 insertions(+), 84 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index 4f51277524..c5383a478e 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -7,6 +7,7 @@ use futures_util::stream::FuturesUnordered; use helpers::{http_client, ws_client, SUB_METHOD_NAME, UNSUB_METHOD_NAME}; use jsonrpsee::core::client::{ClientT, SubscriptionClientT}; use jsonrpsee::http_client::HeaderMap; +use jsonrpsee::types::params::EmptyParams; use jsonrpsee::types::{BatchRequestBuilder, Id, ParamsSer, RequestSer, ToRpcParams, UnnamedParamsBuilder}; use pprof::criterion::{Output, PProfProfiler}; use tokio::runtime::Runtime as TokioRuntime; @@ -135,7 +136,7 @@ fn round_trip(rt: &TokioRuntime, crit: &mut Criterion, client: Arc let bench_name = format!("{}/{}", name, method); crit.bench_function(&request.group_name(&bench_name), |b| { b.to_async(rt).iter(|| async { - black_box(client.request::(method, ()).await.unwrap()); + black_box(client.request::(method, EmptyParams).await.unwrap()); }) }); } @@ -145,7 +146,9 @@ fn sub_round_trip(rt: &TokioRuntime, crit: &mut Criterion, client: Arc(SUB_METHOD_NAME, (), UNSUB_METHOD_NAME).await.unwrap()); + black_box( + client.subscribe::(SUB_METHOD_NAME, EmptyParams, UNSUB_METHOD_NAME).await.unwrap(), + ); }) }); group.bench_function("subscribe_response", |b| { @@ -155,7 +158,10 @@ fn sub_round_trip(rt: &TokioRuntime, crit: &mut Criterion, client: Arc(SUB_METHOD_NAME, (), UNSUB_METHOD_NAME).await.unwrap() + client + .subscribe::(SUB_METHOD_NAME, EmptyParams, UNSUB_METHOD_NAME) + .await + .unwrap() }) }) }, @@ -172,7 +178,10 @@ fn sub_round_trip(rt: &TokioRuntime, crit: &mut Criterion, client: Arc(SUB_METHOD_NAME, (), UNSUB_METHOD_NAME).await.unwrap() + client + .subscribe::(SUB_METHOD_NAME, EmptyParams, UNSUB_METHOD_NAME) + .await + .unwrap() }) }, |sub| { @@ -199,7 +208,7 @@ fn batch_round_trip( for batch_size in [2, 5, 10, 50, 100usize].iter() { let mut batch = BatchRequestBuilder::new(); for _ in 0..*batch_size { - batch.insert(method, ()).unwrap(); + batch.insert(method, EmptyParams).unwrap(); } group.throughput(Throughput::Elements(*batch_size as u64)); group.bench_with_input(BenchmarkId::from_parameter(batch_size), batch_size, |b, _| { @@ -236,7 +245,7 @@ fn ws_concurrent_conn_calls(rt: &TokioRuntime, crit: &mut Criterion, url: &str, let futs = FuturesUnordered::new(); for _ in 0..10 { - futs.push(client.request::(methods[0], ())); + futs.push(client.request::(methods[0], EmptyParams)); } join_all(futs).await; @@ -276,13 +285,13 @@ fn ws_concurrent_conn_subs(rt: &TokioRuntime, crit: &mut Criterion, url: &str, n let futs = FuturesUnordered::new(); for _ in 0..10 { - let fut = client.subscribe::(SUB_METHOD_NAME, (), UNSUB_METHOD_NAME).then( - |sub| async move { + let fut = client + .subscribe::(SUB_METHOD_NAME, EmptyParams, UNSUB_METHOD_NAME) + .then(|sub| async move { let mut s = sub.unwrap(); s.next().await.unwrap().unwrap() - }, - ); + }); futs.push(Box::pin(fut)); } @@ -310,7 +319,7 @@ fn http_concurrent_conn_calls(rt: &TokioRuntime, crit: &mut Criterion, url: &str |clients| async { let tasks = clients.map(|client| { rt.spawn(async move { - client.request::(method, ()).await.unwrap(); + client.request::(method, EmptyParams).await.unwrap(); }) }); join_all(tasks).await; @@ -342,7 +351,7 @@ fn http_custom_headers_round_trip( crit.bench_function(&request.group_name(&bench_name), |b| { b.to_async(rt).iter(|| async { - black_box(client.request::(method_name, ()).await.unwrap()); + black_box(client.request::(method_name, EmptyParams).await.unwrap()); }) }); } diff --git a/client/http-client/src/tests.rs b/client/http-client/src/tests.rs index 37cd8a8194..7115d8a054 100644 --- a/client/http-client/src/tests.rs +++ b/client/http-client/src/tests.rs @@ -35,7 +35,6 @@ use jsonrpsee_test_utils::mocks::Id; use jsonrpsee_test_utils::TimeoutFutureExt; use jsonrpsee_types::error::{CallError, ErrorObjectOwned}; use jsonrpsee_types::BatchRequestBuilder; -use serde_json::value::RawValue; #[tokio::test] async fn method_call_works() { diff --git a/client/ws-client/src/tests.rs b/client/ws-client/src/tests.rs index f86d23856b..bd5f24e301 100644 --- a/client/ws-client/src/tests.rs +++ b/client/ws-client/src/tests.rs @@ -36,7 +36,6 @@ use jsonrpsee_test_utils::mocks::{Id, WebSocketTestServer}; use jsonrpsee_test_utils::TimeoutFutureExt; use jsonrpsee_types::error::{CallError, ErrorObjectOwned}; use jsonrpsee_types::BatchRequestBuilder; -use serde_json::value::RawValue; use serde_json::Value as JsonValue; #[tokio::test] diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index a911bdfba7..cab6cd714b 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -50,6 +50,8 @@ pub mod __reexports { pub use jsonrpsee_types::ToRpcParams; // Main builder object for constructing the rpc parameters. pub use jsonrpsee_types::UnnamedParamsBuilder; + // Empty rpc parameters for empty macro. + pub use jsonrpsee_types::EmptyParams; } cfg_async_client! { @@ -189,8 +191,7 @@ pub trait TransportReceiverT: 'static { #[macro_export] macro_rules! rpc_params { () => { - // ToRpcParams is implemented for the empty tuple. - () + $crate::client::__reexports::EmptyParams }; ($($param:expr),*) => { { diff --git a/core/src/server/rpc_module.rs b/core/src/server/rpc_module.rs index 19e903ba06..fe714b7a28 100644 --- a/core/src/server/rpc_module.rs +++ b/core/src/server/rpc_module.rs @@ -459,7 +459,7 @@ impl Methods { /// ``` /// #[tokio::main] /// async fn main() { - /// use jsonrpsee::{RpcModule, types::EmptyParams}; + /// use jsonrpsee::{RpcModule, types::EmptyServerParams}; /// /// let mut module = RpcModule::new(()); /// module.register_subscription("hi", "hi", "goodbye", |_, mut sink, _| { @@ -467,7 +467,7 @@ impl Methods { /// Ok(()) /// }).unwrap(); /// - /// let mut sub = module.subscribe("hi", EmptyParams::new()).await.unwrap(); + /// let mut sub = module.subscribe("hi", EmptyServerParams::new()).await.unwrap(); /// // In this case we ignore the subscription ID, /// let (sub_resp, _sub_id) = sub.next::().await.unwrap().unwrap(); /// assert_eq!(&sub_resp, "one answer"); diff --git a/proc-macros/src/render_client.rs b/proc-macros/src/render_client.rs index 7bb007ca1e..93c5028f8e 100644 --- a/proc-macros/src/render_client.rs +++ b/proc-macros/src/render_client.rs @@ -150,12 +150,14 @@ impl RpcDescription { param_kind: &ParamKind, signature: &syn::TraitItemMethod, ) -> TokenStream2 { + let jsonrpsee = self.jsonrpsee_client_path.as_ref().unwrap(); + if params.is_empty() { - return quote!({}); + return quote!({ + #jsonrpsee::types::EmptyParams + }); } - let jsonrpsee = self.jsonrpsee_client_path.as_ref().unwrap(); - match param_kind { ParamKind::Map => { // Extract parameter names. diff --git a/proc-macros/tests/ui/correct/basic.rs b/proc-macros/tests/ui/correct/basic.rs index c538d467e2..a59cadc129 100644 --- a/proc-macros/tests/ui/correct/basic.rs +++ b/proc-macros/tests/ui/correct/basic.rs @@ -5,7 +5,7 @@ use std::net::SocketAddr; use jsonrpsee::core::{async_trait, client::ClientT, RpcResult}; use jsonrpsee::proc_macros::rpc; use jsonrpsee::rpc_params; -use jsonrpsee::types::{SubscriptionResult, UnnamedParams}; +use jsonrpsee::types::{SubscriptionResult, UnnamedParams, EmptyParams}; use jsonrpsee::ws_client::*; use jsonrpsee::ws_server::{SubscriptionSink, WsServerBuilder}; @@ -105,7 +105,7 @@ async fn main() { assert_eq!(client.array_params(vec![1]).await.unwrap(), 1); assert_eq!(client.request::("foo_array_params", rpc_params![Vec::::new()]).await.unwrap(), 0); - assert_eq!(client.request::("foo_optional_param", rpc_params![]).await.unwrap(), false); + assert_eq!(client.request::("foo_optional_param", rpc_params![]).await.unwrap(), false); assert_eq!(client.request::("foo_optional_param", rpc_params![1]).await.unwrap(), true); let mut sub = client.sub().await.unwrap(); diff --git a/tests/tests/integration_tests.rs b/tests/tests/integration_tests.rs index 5ee09a0b54..76c0bd6eec 100644 --- a/tests/tests/integration_tests.rs +++ b/tests/tests/integration_tests.rs @@ -40,7 +40,7 @@ use jsonrpsee::http_client::HttpClientBuilder; use jsonrpsee::http_server::AccessControlBuilder; use jsonrpsee::rpc_params; use jsonrpsee::types::error::ErrorObject; -use jsonrpsee::types::{BatchRequestBuilder, UnnamedParams}; +use jsonrpsee::types::{BatchRequestBuilder, EmptyParams, UnnamedParams}; use jsonrpsee::ws_client::WsClientBuilder; use tokio::time::interval; use tokio_stream::wrappers::IntervalStream; @@ -176,8 +176,8 @@ async fn http_concurrent_method_call_limits_works() { let client = HttpClientBuilder::default().max_concurrent_requests(1).build(&uri).unwrap(); let (first, second) = tokio::join!( - client.request::("say_hello", rpc_params!()), - client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params!()), + client.request::("say_hello", rpc_params![]), ); assert!(first.is_ok()); @@ -295,7 +295,7 @@ async fn ws_making_more_requests_than_allowed_should_not_deadlock() { for _ in 0..6 { let c = client.clone(); - requests.push(tokio::spawn(async move { c.request::("say_hello", rpc_params![]).await })); + requests.push(tokio::spawn(async move { c.request::("say_hello", rpc_params![]).await })); } for req in requests { @@ -316,7 +316,7 @@ async fn http_making_more_requests_than_allowed_should_not_deadlock() { for _ in 0..6 { let c = client.clone(); - requests.push(tokio::spawn(async move { c.request::("say_hello", rpc_params![]).await })); + requests.push(tokio::spawn(async move { c.request::("say_hello", rpc_params![]).await })); } for req in requests { @@ -487,7 +487,7 @@ async fn ws_server_notify_client_on_disconnect() { tokio::spawn(async move { let client = WsClientBuilder::default().build(&server_url).await.unwrap(); // Validate server is up. - client.request::("say_hello", rpc_params![]).await.unwrap(); + client.request::("say_hello", rpc_params![]).await.unwrap(); // Signal client is waiting for the server to disconnect. up_tx.send(()).unwrap(); @@ -534,7 +534,7 @@ async fn ws_server_notify_client_on_disconnect_with_closed_server() { let client = WsClientBuilder::default().build(&server_url).await.unwrap(); // Validate server is up. - client.request::("say_hello", rpc_params![]).await.unwrap(); + client.request::("say_hello", rpc_params![]).await.unwrap(); // Stop the server. server_handle.stop().unwrap().await; @@ -554,7 +554,12 @@ async fn ws_server_cancels_subscriptions_on_reset_conn() { let mut subs = Vec::new(); for _ in 0..10 { - subs.push(client.subscribe::("subscribe_sleep", rpc_params![], "unsubscribe_sleep").await.unwrap()); + subs.push( + client + .subscribe::("subscribe_sleep", rpc_params![], "unsubscribe_sleep") + .await + .unwrap(), + ); } // terminate connection. @@ -626,7 +631,9 @@ async fn ws_server_pipe_from_stream_should_cancel_tasks_immediately() { let mut subs = Vec::new(); for _ in 0..10 { - subs.push(client.subscribe::("subscribe_sleep", rpc_params![], "unsubscribe_sleep").await.unwrap()) + subs.push( + client.subscribe::("subscribe_sleep", rpc_params![], "unsubscribe_sleep").await.unwrap(), + ) } // This will call the `unsubscribe method`. @@ -643,8 +650,10 @@ async fn ws_server_pipe_from_stream_can_be_reused() { let (addr, _handle) = websocket_server_with_subscription().await; let client = WsClientBuilder::default().build(&format!("ws://{}", addr)).await.unwrap(); - let sub = - client.subscribe::("can_reuse_subscription", rpc_params![], "u_can_reuse_subscription").await.unwrap(); + let sub = client + .subscribe::("can_reuse_subscription", rpc_params![], "u_can_reuse_subscription") + .await + .unwrap(); let items = sub.fold(0, |acc, _| async move { acc + 1 }).await; @@ -705,12 +714,20 @@ async fn ws_server_limit_subs_per_conn_works() { let mut subs2 = Vec::new(); for _ in 0..10 { - subs1.push(c1.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever").await.unwrap()); - subs2.push(c2.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever").await.unwrap()); + subs1.push( + c1.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever") + .await + .unwrap(), + ); + subs2.push( + c2.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever") + .await + .unwrap(), + ); } - let err1 = c1.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever").await; - let err2 = c1.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever").await; + let err1 = c1.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever").await; + let err2 = c1.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever").await; let data = "\"Exceeded max limit of 10\""; @@ -759,7 +776,10 @@ async fn ws_server_unsub_methods_should_ignore_sub_limit() { let mut subs = Vec::new(); for _ in 0..10 { subs.push( - client.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever").await.unwrap(), + client + .subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever") + .await + .unwrap(), ); } @@ -980,7 +1000,7 @@ async fn ws_host_filtering_wildcard_works() { let server_url = format!("ws://{}", addr); let client = WsClientBuilder::default().build(&server_url).await.unwrap(); - assert!(client.request::("say_hello", rpc_params![]).await.is_ok()); + assert!(client.request::("say_hello", rpc_params![]).await.is_ok()); } #[tokio::test] @@ -1004,5 +1024,5 @@ async fn http_host_filtering_wildcard_works() { let server_url = format!("http://{}", addr); let client = HttpClientBuilder::default().build(&server_url).unwrap(); - assert!(client.request::("say_hello", rpc_params![]).await.is_ok()); + assert!(client.request::("say_hello", rpc_params![]).await.is_ok()); } diff --git a/tests/tests/resource_limiting.rs b/tests/tests/resource_limiting.rs index 6df15b12a2..c519153c61 100644 --- a/tests/tests/resource_limiting.rs +++ b/tests/tests/resource_limiting.rs @@ -34,7 +34,7 @@ use jsonrpsee::http_client::HttpClientBuilder; use jsonrpsee::http_server::{HttpServerBuilder, HttpServerHandle}; use jsonrpsee::proc_macros::rpc; use jsonrpsee::types::error::CallError; -use jsonrpsee::types::SubscriptionResult; +use jsonrpsee::types::{EmptyParams, SubscriptionResult}; use jsonrpsee::ws_client::WsClientBuilder; use jsonrpsee::ws_server::{WsServerBuilder, WsServerHandle}; @@ -186,10 +186,10 @@ async fn run_tests_on_ws_server(server_addr: SocketAddr, server_handle: WsServer // 2 CPU units (default) per call, so 4th call exceeds cap let (pass1, pass2, pass3, fail) = tokio::join!( - client.request::("say_hello", rpc_params!()), - client.request::("say_hello", rpc_params![]), - client.request::("say_hello", rpc_params![]), - client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params!()), + client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params![]), ); assert!(pass1.is_ok()); @@ -199,11 +199,11 @@ async fn run_tests_on_ws_server(server_addr: SocketAddr, server_handle: WsServer // 3 CPU units per call, so 3rd call exceeds CPU cap, but we can still get on MEM let (pass_cpu1, pass_cpu2, fail_cpu, pass_mem, fail_mem) = tokio::join!( - client.request::("expensive_call", rpc_params![]), - client.request::("expensive_call", rpc_params![]), - client.request::("expensive_call", rpc_params![]), - client.request::("memory_hog", rpc_params![]), - client.request::("memory_hog", rpc_params![]), + client.request::("expensive_call", rpc_params![]), + client.request::("expensive_call", rpc_params![]), + client.request::("expensive_call", rpc_params![]), + client.request::("memory_hog", rpc_params![]), + client.request::("memory_hog", rpc_params![]), ); assert!(pass_cpu1.is_ok()); @@ -217,8 +217,8 @@ async fn run_tests_on_ws_server(server_addr: SocketAddr, server_handle: WsServer // // Thus, we can't assume that all subscriptions drop their resources instantly anymore. let (pass1, pass2) = tokio::join!( - client.subscribe::("subscribe_hello", rpc_params![], "unsubscribe_hello"), - client.subscribe::("subscribe_hello", rpc_params![], "unsubscribe_hello"), + client.subscribe::("subscribe_hello", rpc_params![], "unsubscribe_hello"), + client.subscribe::("subscribe_hello", rpc_params![], "unsubscribe_hello"), ); assert!(pass1.is_ok()); @@ -226,9 +226,9 @@ async fn run_tests_on_ws_server(server_addr: SocketAddr, server_handle: WsServer // 3 CPU units (manually set for subscriptions) per call, so 3th call exceeds cap let (pass1, pass2, fail) = tokio::join!( - client.subscribe::("subscribe_hello_limit", rpc_params![], "unsubscribe_hello_limit"), - client.subscribe::("subscribe_hello_limit", rpc_params![], "unsubscribe_hello_limit"), - client.subscribe::("subscribe_hello_limit", rpc_params![], "unsubscribe_hello_limit"), + client.subscribe::("subscribe_hello_limit", rpc_params![], "unsubscribe_hello_limit"), + client.subscribe::("subscribe_hello_limit", rpc_params![], "unsubscribe_hello_limit"), + client.subscribe::("subscribe_hello_limit", rpc_params![], "unsubscribe_hello_limit"), ); assert!(pass1.is_ok()); @@ -244,10 +244,10 @@ async fn run_tests_on_http_server(server_addr: SocketAddr, server_handle: HttpSe // 2 CPU units (default) per call, so 4th call exceeds cap let (a, b, c, d) = tokio::join!( - client.request::("say_hello", rpc_params![]), - client.request::("say_hello", rpc_params![]), - client.request::("say_hello", rpc_params![]), - client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params![]), ); // HTTP does not guarantee ordering diff --git a/tests/tests/rpc_module.rs b/tests/tests/rpc_module.rs index 58aaf702b8..408a14f531 100644 --- a/tests/tests/rpc_module.rs +++ b/tests/tests/rpc_module.rs @@ -31,7 +31,7 @@ use futures::StreamExt; use jsonrpsee::core::error::{Error, SubscriptionClosed}; use jsonrpsee::core::server::rpc_module::*; use jsonrpsee::types::error::{CallError, ErrorCode, ErrorObject, PARSE_ERROR_CODE}; -use jsonrpsee::types::{EmptyParams, Params}; +use jsonrpsee::types::{EmptyServerParams, Params}; use serde::{Deserialize, Serialize}; use tokio::time::interval; use tokio_stream::wrappers::IntervalStream; @@ -98,7 +98,7 @@ async fn calling_method_without_server() { let mut module = RpcModule::new(()); module.register_method("boo", |_: Params, _| Ok(String::from("boo!"))).unwrap(); - let res: String = module.call("boo", EmptyParams::new()).await.unwrap(); + let res: String = module.call("boo", EmptyServerParams::new()).await.unwrap(); assert_eq!(&res, "boo!"); // Call sync method with params @@ -112,7 +112,7 @@ async fn calling_method_without_server() { assert_eq!(res, 6); // Call sync method with bad param - let err = module.call::<_, ()>("foo", (false,)).await.unwrap_err(); + 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" @@ -198,7 +198,7 @@ async fn calling_method_without_server_using_proc_macro() { let module = CoolServerImpl.into_rpc(); // Call sync method with no params - let res: bool = module.call("rebel_without_cause", EmptyParams::new()).await.unwrap(); + let res: bool = module.call("rebel_without_cause", EmptyServerParams::new()).await.unwrap(); assert!(!res); // Call sync method with params @@ -206,7 +206,7 @@ async fn calling_method_without_server_using_proc_macro() { assert_eq!(&res, "0 Gun { shoots: true }"); // Call sync method with bad params - let err = module.call::<_, ()>("rebel", (Gun { shoots: true }, false)).await.unwrap_err(); + 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" )); @@ -253,7 +253,7 @@ async fn subscribing_without_server() { }) .unwrap(); - let mut my_sub = module.subscribe("my_sub", EmptyParams::new()).await.unwrap(); + let mut my_sub = module.subscribe("my_sub", EmptyServerParams::new()).await.unwrap(); for i in (0..=2).rev() { let (val, id) = my_sub.next::().await.unwrap().unwrap(); assert_eq!(val, std::char::from_digit(i, 10).unwrap()); @@ -288,11 +288,11 @@ async fn close_test_subscribing_without_server() { }) .unwrap(); - let mut my_sub = module.subscribe("my_sub", EmptyParams::new()).await.unwrap(); + let mut my_sub = module.subscribe("my_sub", EmptyServerParams::new()).await.unwrap(); let (val, id) = my_sub.next::().await.unwrap().unwrap(); assert_eq!(&val, "lo"); assert_eq!(&id, my_sub.subscription_id()); - let mut my_sub2 = std::mem::ManuallyDrop::new(module.subscribe("my_sub", EmptyParams::new()).await.unwrap()); + let mut my_sub2 = std::mem::ManuallyDrop::new(module.subscribe("my_sub", EmptyServerParams::new()).await.unwrap()); // Close the subscription to ensure it doesn't return any items. my_sub.close(); @@ -332,7 +332,7 @@ async fn subscribing_without_server_bad_params() { }) .unwrap(); - let sub = module.subscribe("my_sub", EmptyParams::new()).await.unwrap_err(); + let sub = module.subscribe("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()) @@ -355,7 +355,7 @@ async fn subscribe_unsubscribe_without_server() { .unwrap(); async fn subscribe_and_assert(module: &RpcModule<()>) { - let sub = module.subscribe("my_sub", EmptyParams::new()).await.unwrap(); + let sub = module.subscribe("my_sub", EmptyServerParams::new()).await.unwrap(); let ser_id = serde_json::to_string(sub.subscription_id()).unwrap(); @@ -390,7 +390,7 @@ async fn empty_subscription_without_server() { }) .unwrap(); - let sub_err = module.subscribe("my_sub", EmptyParams::new()).await.unwrap_err(); + let sub_err = module.subscribe("my_sub", EmptyServerParams::new()).await.unwrap_err(); assert!( matches!(sub_err, Error::Call(CallError::Custom(e)) if e.message().contains("Invalid params") && e.code() == ErrorCode::InvalidParams.code()) ); @@ -407,7 +407,7 @@ async fn rejected_subscription_without_server() { }) .unwrap(); - let sub_err = module.subscribe("my_sub", EmptyParams::new()).await.unwrap_err(); + let sub_err = module.subscribe("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) ); @@ -432,7 +432,7 @@ async fn accepted_twice_subscription_without_server() { }) .unwrap(); - let _ = module.subscribe("my_sub", EmptyParams::new()).await.expect("Subscription should not fail"); + let _ = module.subscribe("my_sub", EmptyServerParams::new()).await.expect("Subscription should not fail"); } #[tokio::test] @@ -455,7 +455,7 @@ async fn reject_twice_subscription_without_server() { }) .unwrap(); - let sub_err = module.subscribe("my_sub", EmptyParams::new()).await.unwrap_err(); + let sub_err = module.subscribe("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) ); diff --git a/types/src/lib.rs b/types/src/lib.rs index ee1318ac25..a2c6467ba0 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -44,11 +44,11 @@ pub mod error; pub use error::{ErrorObject, ErrorObjectOwned, ErrorResponse, SubscriptionEmptyError, SubscriptionResult}; pub use params::{ - BatchRequestBuilder, Id, NamedParams, NamedParamsBuilder, Params, ParamsSequence, ParamsSer, SubscriptionId, - ToRpcParams, TwoPointZero, UnnamedParams, UnnamedParamsBuilder, + BatchRequestBuilder, EmptyParams, Id, NamedParams, NamedParamsBuilder, Params, ParamsSequence, ParamsSer, + SubscriptionId, ToRpcParams, TwoPointZero, UnnamedParams, UnnamedParamsBuilder, }; pub use request::{InvalidRequest, Notification, NotificationSer, Request, RequestSer}; pub use response::{Response, SubscriptionPayload, SubscriptionResponse}; -/// Empty `RpcParams` type. -pub type EmptyParams = Vec<()>; +/// Empty server `RpcParams` type to use while registering modules. +pub type EmptyServerParams = Vec<()>; diff --git a/types/src/params.rs b/types/src/params.rs index 0f1f79cb32..bb4d1d3810 100644 --- a/types/src/params.rs +++ b/types/src/params.rs @@ -674,8 +674,12 @@ impl<'a> BatchRequestBuilder<'a> { } } -/// Custom implementation to provide a type which contains to RPC parameters. -impl ToRpcParams for () { +/// Empty RPC parameters that perform no allocation. +#[derive(Clone, Debug)] +pub struct EmptyParams; + +/// Custom implementation for empty RPC parameters. +impl ToRpcParams for EmptyParams { fn to_rpc_params(self) -> Result>, serde_json::Error> { Ok(None) } diff --git a/types/src/request.rs b/types/src/request.rs index 03f3f5e81a..f536205b4b 100644 --- a/types/src/request.rs +++ b/types/src/request.rs @@ -130,7 +130,7 @@ impl<'a> NotificationSer<'a> { #[cfg(test)] mod test { use super::{Id, InvalidRequest, Notification, NotificationSer, Request, RequestSer, TwoPointZero}; - use crate::{ToRpcParams, UnnamedParamsBuilder}; + use crate::{EmptyParams, ToRpcParams, UnnamedParamsBuilder}; use serde_json::value::RawValue; fn assert_request<'a>(request: Request<'a>, id: Id<'a>, method: &str, params: Option<&str>) { @@ -216,13 +216,18 @@ mod test { // With all fields set. (r#"{"jsonrpc":"2.0","id":1,"method":"subtract","params":[42,23]}"#, Some(&id), params.clone(), method), // Escaped method name. - (r#"{"jsonrpc":"2.0","id":1,"method":"\"m"}"#, Some(&id), ().to_rpc_params().unwrap(), "\"m"), + (r#"{"jsonrpc":"2.0","id":1,"method":"\"m"}"#, Some(&id), EmptyParams.to_rpc_params().unwrap(), "\"m"), // Without ID field. (r#"{"jsonrpc":"2.0","id":null,"method":"subtract","params":[42,23]}"#, None, params, method), // Without params field - (r#"{"jsonrpc":"2.0","id":1,"method":"subtract"}"#, Some(&id), ().to_rpc_params().unwrap(), method), + ( + r#"{"jsonrpc":"2.0","id":1,"method":"subtract"}"#, + Some(&id), + EmptyParams.to_rpc_params().unwrap(), + method, + ), // Without params and ID. - (r#"{"jsonrpc":"2.0","id":null,"method":"subtract"}"#, None, ().to_rpc_params().unwrap(), method), + (r#"{"jsonrpc":"2.0","id":null,"method":"subtract"}"#, None, EmptyParams.to_rpc_params().unwrap(), method), ]; for (ser, id, params, method) in test_vector.iter().cloned() { From 0d8022230795d5b6c6e9ed8b28af68cf13571c73 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 31 Aug 2022 11:55:58 +0300 Subject: [PATCH 22/41] tests: Fix macos disabled test Signed-off-by: Alexandru Vasile --- tests/tests/proc_macros.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/tests/proc_macros.rs b/tests/tests/proc_macros.rs index 4f72421b9b..47e1065078 100644 --- a/tests/tests/proc_macros.rs +++ b/tests/tests/proc_macros.rs @@ -300,12 +300,12 @@ async fn macro_zero_copy_cow() { #[cfg(not(target_os = "macos"))] #[tokio::test] async fn multiple_blocking_calls_overlap() { - use jsonrpsee::types::EmptyParams; + use jsonrpsee::types::EmptyServerParams; use std::time::{Duration, Instant}; let module = RpcServerImpl.into_rpc(); - let futures = std::iter::repeat_with(|| module.call::<_, u64>("foo_blocking_call", EmptyParams::new())).take(4); + let futures = std::iter::repeat_with(|| module.call::<_, u64>("foo_blocking_call", EmptyServerParams::new())).take(4); let now = Instant::now(); let results = futures::future::join_all(futures).await; let elapsed = now.elapsed(); From 20e357b2913f58812afdd35a41f960ac43801bb3 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 31 Aug 2022 11:56:45 +0300 Subject: [PATCH 23/41] types: Improve comment Signed-off-by: Alexandru Vasile --- types/src/params.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/src/params.rs b/types/src/params.rs index bb4d1d3810..eeefc5900b 100644 --- a/types/src/params.rs +++ b/types/src/params.rs @@ -484,7 +484,7 @@ mod params_builder { self.bytes.push(self.end as u8); } - // Safety: This is safe because we do not emit invalid UTF-8. + // Safety: This is safe because JSON does not emit invalid UTF-8. unsafe { String::from_utf8_unchecked(self.bytes) } } } From 9590d1e39736c97389cead8b035f7994a64f38e4 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 31 Aug 2022 11:58:15 +0300 Subject: [PATCH 24/41] Fix clippy Signed-off-by: Alexandru Vasile --- tests/tests/proc_macros.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/tests/proc_macros.rs b/tests/tests/proc_macros.rs index 47e1065078..80c310978d 100644 --- a/tests/tests/proc_macros.rs +++ b/tests/tests/proc_macros.rs @@ -305,7 +305,8 @@ async fn multiple_blocking_calls_overlap() { let module = RpcServerImpl.into_rpc(); - let futures = std::iter::repeat_with(|| module.call::<_, u64>("foo_blocking_call", EmptyServerParams::new())).take(4); + let futures = + std::iter::repeat_with(|| module.call::<_, u64>("foo_blocking_call", EmptyServerParams::new())).take(4); let now = Instant::now(); let results = futures::future::join_all(futures).await; let elapsed = now.elapsed(); From 4789f9e44d362259321ca279621d9eca46d0347c Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 31 Aug 2022 14:45:46 +0300 Subject: [PATCH 25/41] benches: Rename functions Signed-off-by: Alexandru Vasile --- benches/bench.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index c5383a478e..e81aaa8464 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -64,7 +64,8 @@ fn v2_serialize(req: RequestSer<'_>) -> String { } pub fn jsonrpsee_types_v2(crit: &mut Criterion) { - crit.bench_function("jsonrpsee_types_v2_array_ref", |b| { + // Construct the serialized request using the `ParamsSer` directly. + crit.bench_function("jsonrpsee_types_baseline_params", |b| { b.iter(|| { let params = &[1_u64.into(), 2_u32.into()]; let params = ParamsSer::ArrayRef(params); @@ -76,7 +77,8 @@ pub fn jsonrpsee_types_v2(crit: &mut Criterion) { }) }); - crit.bench_function("jsonrpsee_types_v2_vec", |b| { + // Construct the serialized request using the `UnnamedParamsBuilder`. + crit.bench_function("jsonrpsee_types_unnamed_params", |b| { b.iter(|| { let mut builder = UnnamedParamsBuilder::new(); builder.insert(1u64).unwrap(); From 6f097b7eef627e67653169b037ea11d80028aa82 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 31 Aug 2022 15:12:06 +0300 Subject: [PATCH 26/41] types: Rename param types to `ArrayParams` and `ObjectParams` Signed-off-by: Alexandru Vasile --- benches/bench.rs | 6 +-- core/src/client/async_client/helpers.rs | 4 +- core/src/client/mod.rs | 8 ++-- proc-macros/src/render_client.rs | 4 +- proc-macros/tests/ui/correct/basic.rs | 6 +-- tests/tests/integration_tests.rs | 4 +- tests/tests/proc_macros.rs | 14 +++---- types/src/lib.rs | 4 +- types/src/params.rs | 50 ++++++++++++------------- types/src/request.rs | 6 +-- 10 files changed, 53 insertions(+), 53 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index e81aaa8464..95fbd3080a 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -8,7 +8,7 @@ use helpers::{http_client, ws_client, SUB_METHOD_NAME, UNSUB_METHOD_NAME}; use jsonrpsee::core::client::{ClientT, SubscriptionClientT}; use jsonrpsee::http_client::HeaderMap; use jsonrpsee::types::params::EmptyParams; -use jsonrpsee::types::{BatchRequestBuilder, Id, ParamsSer, RequestSer, ToRpcParams, UnnamedParamsBuilder}; +use jsonrpsee::types::{ArrayParamsBuilder, BatchRequestBuilder, Id, ParamsSer, RequestSer, ToRpcParams}; use pprof::criterion::{Output, PProfProfiler}; use tokio::runtime::Runtime as TokioRuntime; @@ -77,10 +77,10 @@ pub fn jsonrpsee_types_v2(crit: &mut Criterion) { }) }); - // Construct the serialized request using the `UnnamedParamsBuilder`. + // Construct the serialized request using the `ArrayParamsBuilder`. crit.bench_function("jsonrpsee_types_unnamed_params", |b| { b.iter(|| { - let mut builder = UnnamedParamsBuilder::new(); + let mut builder = ArrayParamsBuilder::new(); builder.insert(1u64).unwrap(); builder.insert(2u32).unwrap(); let params = builder.build().to_rpc_params().expect("Valid params"); diff --git a/core/src/client/async_client/helpers.rs b/core/src/client/async_client/helpers.rs index a0bfd7fab2..f030d5620a 100644 --- a/core/src/client/async_client/helpers.rs +++ b/core/src/client/async_client/helpers.rs @@ -34,7 +34,7 @@ use futures_util::future::{self, Either}; use jsonrpsee_types::error::CallError; use jsonrpsee_types::response::SubscriptionError; -use jsonrpsee_types::{ErrorResponse, Id, Notification, RequestSer, Response, SubscriptionId, SubscriptionResponse, ToRpcParams, UnnamedParamsBuilder}; +use jsonrpsee_types::{ErrorResponse, Id, Notification, RequestSer, Response, SubscriptionId, SubscriptionResponse, ToRpcParams, ArrayParamsBuilder}; use serde_json::Value as JsonValue; /// Attempts to process a batch response. @@ -221,7 +221,7 @@ pub(crate) fn build_unsubscribe_message( ) -> Option { let (unsub_req_id, _, unsub, sub_id) = manager.remove_subscription(sub_req_id, sub_id)?; - let mut params = UnnamedParamsBuilder::new(); + let mut params = ArrayParamsBuilder::new(); params.insert(sub_id).ok()?; let params = params.build().to_rpc_params().ok()?; diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index cab6cd714b..ac534ab2fc 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -46,10 +46,10 @@ use jsonrpsee_types::params::ToRpcParams; // Re-exports for the `rpc_params` macro. #[doc(hidden)] pub mod __reexports { - // Needs to be in scope for `UnnamedParams` to implement it. + // Needs to be in scope for `ArrayParams` to implement it. pub use jsonrpsee_types::ToRpcParams; // Main builder object for constructing the rpc parameters. - pub use jsonrpsee_types::UnnamedParamsBuilder; + pub use jsonrpsee_types::ArrayParamsBuilder; // Empty rpc parameters for empty macro. pub use jsonrpsee_types::EmptyParams; } @@ -182,7 +182,7 @@ pub trait TransportReceiverT: 'static { async fn receive(&mut self) -> Result; } -/// Convert the given values to a [`jsonrpsee_types::UnnamedParams`], or empty tuple as expected by a +/// Convert the given values to a [`jsonrpsee_types::ArrayParamsBuilder`], or empty tuple as expected by a /// jsonrpsee Client (http or websocket). /// /// # Panics @@ -195,7 +195,7 @@ macro_rules! rpc_params { }; ($($param:expr),*) => { { - let mut __params = $crate::client::__reexports::UnnamedParamsBuilder::new(); + let mut __params = $crate::client::__reexports::ArrayParamsBuilder::new(); $( __params.insert($param).expect(format!("Parameter `{}` cannot be serialized", stringify!($param)).as_str()); )* diff --git a/proc-macros/src/render_client.rs b/proc-macros/src/render_client.rs index 93c5028f8e..c86bd39fee 100644 --- a/proc-macros/src/render_client.rs +++ b/proc-macros/src/render_client.rs @@ -170,7 +170,7 @@ impl RpcDescription { quote!(#name, #value) }); quote!({ - let mut builder = #jsonrpsee::types::NamedParamsBuilder::new(); + let mut builder = #jsonrpsee::types::ObjectParamsBuilder::new(); #( builder.insert( #params_insert ).expect(format!("Parameters {} must be valid", stringify!(#params_insert)).as_str()); )* builder.build() }) @@ -179,7 +179,7 @@ impl RpcDescription { // Throw away the type. let params = params.iter().map(|(param, _param_type)| param); quote!({ - let mut builder = #jsonrpsee::types::UnnamedParamsBuilder::new(); + let mut builder = #jsonrpsee::types::ArrayParamsBuilder::new(); #( builder.insert( #params ).expect(format!("Parameters {} must be valid", stringify!(#params)).as_str()); )* builder.build() }) diff --git a/proc-macros/tests/ui/correct/basic.rs b/proc-macros/tests/ui/correct/basic.rs index a59cadc129..ff9496db0f 100644 --- a/proc-macros/tests/ui/correct/basic.rs +++ b/proc-macros/tests/ui/correct/basic.rs @@ -5,7 +5,7 @@ use std::net::SocketAddr; use jsonrpsee::core::{async_trait, client::ClientT, RpcResult}; use jsonrpsee::proc_macros::rpc; use jsonrpsee::rpc_params; -use jsonrpsee::types::{SubscriptionResult, UnnamedParams, EmptyParams}; +use jsonrpsee::types::{SubscriptionResult, ArrayParams, EmptyParams}; use jsonrpsee::ws_client::*; use jsonrpsee::ws_server::{SubscriptionSink, WsServerBuilder}; @@ -103,10 +103,10 @@ async fn main() { assert_eq!(client.optional_params(Some(1), "a".into()).await.unwrap(), true); assert_eq!(client.array_params(vec![1]).await.unwrap(), 1); - assert_eq!(client.request::("foo_array_params", rpc_params![Vec::::new()]).await.unwrap(), 0); + assert_eq!(client.request::("foo_array_params", rpc_params![Vec::::new()]).await.unwrap(), 0); assert_eq!(client.request::("foo_optional_param", rpc_params![]).await.unwrap(), false); - assert_eq!(client.request::("foo_optional_param", rpc_params![1]).await.unwrap(), true); + assert_eq!(client.request::("foo_optional_param", rpc_params![1]).await.unwrap(), true); let mut sub = client.sub().await.unwrap(); let first_recv = sub.next().await.transpose().unwrap(); diff --git a/tests/tests/integration_tests.rs b/tests/tests/integration_tests.rs index 76c0bd6eec..1b46d982ec 100644 --- a/tests/tests/integration_tests.rs +++ b/tests/tests/integration_tests.rs @@ -40,7 +40,7 @@ use jsonrpsee::http_client::HttpClientBuilder; use jsonrpsee::http_server::AccessControlBuilder; use jsonrpsee::rpc_params; use jsonrpsee::types::error::ErrorObject; -use jsonrpsee::types::{BatchRequestBuilder, EmptyParams, UnnamedParams}; +use jsonrpsee::types::{ArrayParams, BatchRequestBuilder, EmptyParams}; use jsonrpsee::ws_client::WsClientBuilder; use tokio::time::interval; use tokio_stream::wrappers::IntervalStream; @@ -952,7 +952,7 @@ async fn ws_subscribe_with_bad_params() { let client = WsClientBuilder::default().build(&server_url).await.unwrap(); let err = client - .subscribe::("subscribe_add_one", rpc_params!["0x0"], "unsubscribe_add_one") + .subscribe::("subscribe_add_one", rpc_params!["0x0"], "unsubscribe_add_one") .await .unwrap_err(); assert!(matches!(err, Error::Call(_))); diff --git a/tests/tests/proc_macros.rs b/tests/tests/proc_macros.rs index 80c310978d..41b2f62bd5 100644 --- a/tests/tests/proc_macros.rs +++ b/tests/tests/proc_macros.rs @@ -196,7 +196,7 @@ mod rpc_impl { } // Use generated implementations of server and client. -use crate::types::{NamedParams, NamedParamsBuilder, UnnamedParams}; +use crate::types::{ArrayParams, ObjectParams, ObjectParamsBuilder}; use rpc_impl::{RpcClient, RpcServer, RpcServerImpl}; pub async fn websocket_server() -> SocketAddr { @@ -342,7 +342,7 @@ async fn calls_with_bad_params() { // Sub with faulty params as array. let err: Error = client - .subscribe::("foo_echo", rpc_params!["0x0"], "foo_unsubscribe_echo") + .subscribe::("foo_echo", rpc_params!["0x0"], "foo_unsubscribe_echo") .await .unwrap_err(); assert!( @@ -350,29 +350,29 @@ async fn calls_with_bad_params() { ); // Call with faulty params as array. - let err: Error = client.request::("foo_foo", rpc_params!["faulty", "ok"]).await.unwrap_err(); + 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()) ); // Sub with faulty params as map. - let mut params = NamedParamsBuilder::new(); + let mut params = ObjectParamsBuilder::new(); params.insert("val", "0x0").unwrap(); let params = params.build(); let err: Error = - client.subscribe::("foo_echo", params, "foo_unsubscribe_echo").await.unwrap_err(); + 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()) ); // Call with faulty params as map. - let mut params = NamedParamsBuilder::new(); + let mut params = ObjectParamsBuilder::new(); params.insert("param_a", 1).unwrap(); params.insert("param_b", 2).unwrap(); let params = params.build(); - let err: Error = client.request::("foo_foo", params).await.unwrap_err(); + 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()) ); diff --git a/types/src/lib.rs b/types/src/lib.rs index a2c6467ba0..712daf0456 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -44,8 +44,8 @@ pub mod error; pub use error::{ErrorObject, ErrorObjectOwned, ErrorResponse, SubscriptionEmptyError, SubscriptionResult}; pub use params::{ - BatchRequestBuilder, EmptyParams, Id, NamedParams, NamedParamsBuilder, Params, ParamsSequence, ParamsSer, - SubscriptionId, ToRpcParams, TwoPointZero, UnnamedParams, UnnamedParamsBuilder, + ArrayParams, ArrayParamsBuilder, BatchRequestBuilder, EmptyParams, Id, ObjectParams, ObjectParamsBuilder, Params, + ParamsSequence, ParamsSer, SubscriptionId, ToRpcParams, TwoPointZero, }; pub use request::{InvalidRequest, Notification, NotificationSer, Request, RequestSer}; pub use response::{Response, SubscriptionPayload, SubscriptionResponse}; diff --git a/types/src/params.rs b/types/src/params.rs index eeefc5900b..29e9dc1446 100644 --- a/types/src/params.rs +++ b/types/src/params.rs @@ -41,9 +41,9 @@ use serde_json::Value as JsonValue; #[doc(hidden)] pub mod __reexports { + pub use crate::params::ArrayParams; + pub use crate::params::ArrayParamsBuilder; pub use crate::params::ToRpcParams; - pub use crate::params::UnnamedParams; - pub use crate::params::UnnamedParamsBuilder; } /// JSON-RPC v2 marker type. @@ -497,9 +497,9 @@ mod params_builder { /// /// ```rust /// -/// use jsonrpsee_types::NamedParamsBuilder; +/// use jsonrpsee_types::ObjectParamsBuilder; /// -/// let mut builder = NamedParamsBuilder::new(); +/// let mut builder = ObjectParamsBuilder::new(); /// builder.insert("param1", 1); /// builder.insert("param2", "abc"); /// let params = builder.build(); @@ -507,10 +507,10 @@ mod params_builder { /// // Use RPC parameters... /// ``` #[derive(Debug)] -pub struct NamedParamsBuilder(params_builder::ParamsBuilder); +pub struct ObjectParamsBuilder(params_builder::ParamsBuilder); -impl NamedParamsBuilder { - /// Construct a new [`NamedParamsBuilder`]. +impl ObjectParamsBuilder { + /// Construct a new [`ObjectParamsBuilder`]. pub fn new() -> Self { Self::default() } @@ -522,22 +522,22 @@ impl NamedParamsBuilder { } /// Finish the building process and return a JSON compatible string. - pub fn build(self) -> NamedParams { - NamedParams(self.0.build()) + pub fn build(self) -> ObjectParams { + ObjectParams(self.0.build()) } } -impl Default for NamedParamsBuilder { +impl Default for ObjectParamsBuilder { fn default() -> Self { Self(params_builder::ParamsBuilder::named()) } } -/// Named RPC parameters stored as a JSON Map object `{ key: value }`. +/// Object RPC parameters stored as a JSON Map object `{ key: value }`. #[derive(Clone, Debug)] -pub struct NamedParams(String); +pub struct ObjectParams(String); -impl ToRpcParams for NamedParams { +impl ToRpcParams for ObjectParams { fn to_rpc_params(self) -> Result>, serde_json::Error> { RawValue::from_string(self.0).map(Some) } @@ -550,9 +550,9 @@ impl ToRpcParams for NamedParams { /// /// ```rust /// -/// use jsonrpsee_types::UnnamedParamsBuilder; +/// use jsonrpsee_types::ArrayParamsBuilder; /// -/// let mut builder = UnnamedParamsBuilder::new(); +/// let mut builder = ArrayParamsBuilder::new(); /// builder.insert("param1"); /// builder.insert(1); /// let params = builder.build(); @@ -560,10 +560,10 @@ impl ToRpcParams for NamedParams { /// // Use RPC parameters... /// ``` #[derive(Debug)] -pub struct UnnamedParamsBuilder(params_builder::ParamsBuilder); +pub struct ArrayParamsBuilder(params_builder::ParamsBuilder); -impl UnnamedParamsBuilder { - /// Construct a new [`UnnamedParamsBuilder`]. +impl ArrayParamsBuilder { + /// Construct a new [`ArrayParamsBuilder`]. pub fn new() -> Self { Self::default() } @@ -574,22 +574,22 @@ impl UnnamedParamsBuilder { } /// Finish the building process and return a JSON compatible string. - pub fn build(self) -> UnnamedParams { - UnnamedParams(self.0.build()) + pub fn build(self) -> ArrayParams { + ArrayParams(self.0.build()) } } -impl Default for UnnamedParamsBuilder { +impl Default for ArrayParamsBuilder { fn default() -> Self { Self(params_builder::ParamsBuilder::positional()) } } -/// Unnamed RPC parameters stored as a JSON Array object `[ value0, value1, .., valueN ]`. +/// Array RPC parameters stored as a JSON Array object `[ value0, value1, .., valueN ]`. #[derive(Clone, Debug)] -pub struct UnnamedParams(String); +pub struct ArrayParams(String); -impl ToRpcParams for UnnamedParams { +impl ToRpcParams for ArrayParams { fn to_rpc_params(self) -> Result>, serde_json::Error> { RawValue::from_string(self.0).map(Some) } @@ -601,7 +601,7 @@ impl ToRpcParams for UnnamedParams { /// /// # Note /// -/// Please consider using the [`UnnamedParamsBuilder`] and [`NamedParamsBuilder`] than +/// Please consider using the [`ArrayParamsBuilder`] and [`ObjectParamsBuilder`] than /// implementing this trait. /// /// # Examples diff --git a/types/src/request.rs b/types/src/request.rs index f536205b4b..f5a0210aef 100644 --- a/types/src/request.rs +++ b/types/src/request.rs @@ -130,7 +130,7 @@ impl<'a> NotificationSer<'a> { #[cfg(test)] mod test { use super::{Id, InvalidRequest, Notification, NotificationSer, Request, RequestSer, TwoPointZero}; - use crate::{EmptyParams, ToRpcParams, UnnamedParamsBuilder}; + use crate::{ArrayParamsBuilder, EmptyParams, ToRpcParams}; use serde_json::value::RawValue; fn assert_request<'a>(request: Request<'a>, id: Id<'a>, method: &str, params: Option<&str>) { @@ -207,7 +207,7 @@ mod test { fn serialize_call() { let method = "subtract"; let id = Id::Number(1); // It's enough to check one variant, since the type itself also has tests. - let mut builder = UnnamedParamsBuilder::new(); + let mut builder = ArrayParamsBuilder::new(); builder.insert(42).unwrap(); builder.insert(23).unwrap(); let params = builder.build().to_rpc_params().unwrap(); // Same as above. @@ -246,7 +246,7 @@ mod test { #[test] fn serialize_notif() { let exp = r#"{"jsonrpc":"2.0","method":"say_hello","params":["hello"]}"#; - let mut builder = UnnamedParamsBuilder::new(); + let mut builder = ArrayParamsBuilder::new(); builder.insert("hello").unwrap(); let params = builder.build().to_rpc_params().unwrap(); let req = NotificationSer::new("say_hello", params); From 7df0f55eca9ca4b2af8c93f1cb503941f7caebf2 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 31 Aug 2022 18:12:11 +0300 Subject: [PATCH 27/41] Move paramters to core crate Signed-off-by: Alexandru Vasile --- benches/bench.rs | 4 +- client/http-client/src/client.rs | 3 +- client/http-client/src/tests.rs | 2 +- client/ws-client/src/tests.rs | 2 +- core/src/client/async_client/helpers.rs | 3 +- core/src/client/async_client/mod.rs | 4 +- core/src/client/mod.rs | 10 +- core/src/lib.rs | 3 + core/src/params.rs | 299 ++++++++++++++++++++++++ proc-macros/src/render_client.rs | 6 +- proc-macros/tests/ui/correct/basic.rs | 3 +- tests/tests/integration_tests.rs | 2 +- tests/tests/proc_macros.rs | 2 +- tests/tests/resource_limiting.rs | 3 +- types/src/lib.rs | 5 +- types/src/params.rs | 277 ---------------------- types/src/request.rs | 21 +- 17 files changed, 331 insertions(+), 318 deletions(-) create mode 100644 core/src/params.rs diff --git a/benches/bench.rs b/benches/bench.rs index 95fbd3080a..58ab928914 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -6,9 +6,9 @@ use futures_util::future::{join_all, FutureExt}; use futures_util::stream::FuturesUnordered; use helpers::{http_client, ws_client, SUB_METHOD_NAME, UNSUB_METHOD_NAME}; use jsonrpsee::core::client::{ClientT, SubscriptionClientT}; +use jsonrpsee::core::params::{ArrayParamsBuilder, BatchRequestBuilder, EmptyParams, ToRpcParams}; use jsonrpsee::http_client::HeaderMap; -use jsonrpsee::types::params::EmptyParams; -use jsonrpsee::types::{ArrayParamsBuilder, BatchRequestBuilder, Id, ParamsSer, RequestSer, ToRpcParams}; +use jsonrpsee::types::{Id, ParamsSer, RequestSer}; use pprof::criterion::{Output, PProfProfiler}; use tokio::runtime::Runtime as TokioRuntime; diff --git a/client/http-client/src/client.rs b/client/http-client/src/client.rs index fdb6397209..792c0cc1cd 100644 --- a/client/http-client/src/client.rs +++ b/client/http-client/src/client.rs @@ -32,11 +32,10 @@ use crate::types::{ErrorResponse, Id, NotificationSer, RequestSer, Response}; use async_trait::async_trait; use hyper::http::HeaderMap; use jsonrpsee_core::client::{CertificateStore, ClientT, IdKind, RequestIdManager, Subscription, SubscriptionClientT}; +use jsonrpsee_core::params::{BatchRequestBuilder, ToRpcParams}; use jsonrpsee_core::tracing::RpcTracing; use jsonrpsee_core::{Error, TEN_MB_SIZE_BYTES}; use jsonrpsee_types::error::CallError; -use jsonrpsee_types::params::ToRpcParams; -use jsonrpsee_types::BatchRequestBuilder; use rustc_hash::FxHashMap; use serde::de::DeserializeOwned; use tracing_futures::Instrument; diff --git a/client/http-client/src/tests.rs b/client/http-client/src/tests.rs index 7115d8a054..83dea9bc9f 100644 --- a/client/http-client/src/tests.rs +++ b/client/http-client/src/tests.rs @@ -28,13 +28,13 @@ use crate::types::error::{ErrorCode, ErrorObject}; use crate::HttpClientBuilder; use jsonrpsee_core::client::{ClientT, IdKind}; +use jsonrpsee_core::params::BatchRequestBuilder; use jsonrpsee_core::rpc_params; use jsonrpsee_core::Error; 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::BatchRequestBuilder; #[tokio::test] async fn method_call_works() { diff --git a/client/ws-client/src/tests.rs b/client/ws-client/src/tests.rs index bd5f24e301..f1cd882d71 100644 --- a/client/ws-client/src/tests.rs +++ b/client/ws-client/src/tests.rs @@ -30,12 +30,12 @@ use crate::types::error::{ErrorCode, ErrorObject}; use crate::WsClientBuilder; use jsonrpsee_core::client::{ClientT, SubscriptionClientT}; use jsonrpsee_core::client::{IdKind, Subscription}; +use jsonrpsee_core::params::BatchRequestBuilder; use jsonrpsee_core::{rpc_params, 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::BatchRequestBuilder; use serde_json::Value as JsonValue; #[tokio::test] diff --git a/core/src/client/async_client/helpers.rs b/core/src/client/async_client/helpers.rs index f030d5620a..0a5b841fd8 100644 --- a/core/src/client/async_client/helpers.rs +++ b/core/src/client/async_client/helpers.rs @@ -34,8 +34,9 @@ use futures_util::future::{self, Either}; use jsonrpsee_types::error::CallError; use jsonrpsee_types::response::SubscriptionError; -use jsonrpsee_types::{ErrorResponse, Id, Notification, RequestSer, Response, SubscriptionId, SubscriptionResponse, ToRpcParams, ArrayParamsBuilder}; +use jsonrpsee_types::{ErrorResponse, Id, Notification, RequestSer, Response, SubscriptionId, SubscriptionResponse}; use serde_json::Value as JsonValue; +use crate::params::{ArrayParamsBuilder, ToRpcParams}; /// Attempts to process a batch response. /// diff --git a/core/src/client/async_client/mod.rs b/core/src/client/async_client/mod.rs index 9e060d0087..5d494d45cb 100644 --- a/core/src/client/async_client/mod.rs +++ b/core/src/client/async_client/mod.rs @@ -28,11 +28,11 @@ use futures_util::stream::StreamExt; use futures_util::FutureExt; use jsonrpsee_types::{ response::SubscriptionError, ErrorResponse, Id, Notification, NotificationSer, RequestSer, Response, - SubscriptionResponse, BatchRequestBuilder + SubscriptionResponse, }; use serde::de::DeserializeOwned; use tracing_futures::Instrument; -use jsonrpsee_types::params::ToRpcParams; +use crate::params::{BatchRequestBuilder, ToRpcParams}; use super::{FrontToBack, IdKind, RequestIdManager}; diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index ac534ab2fc..629feade43 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -38,20 +38,20 @@ use futures_channel::{mpsc, oneshot}; use futures_util::future::FutureExt; use futures_util::sink::SinkExt; use futures_util::stream::{Stream, StreamExt}; -use jsonrpsee_types::{BatchRequestBuilder, Id, SubscriptionId}; +use jsonrpsee_types::{Id, SubscriptionId}; use serde::de::DeserializeOwned; use serde_json::Value as JsonValue; -use jsonrpsee_types::params::ToRpcParams; +use crate::params::{BatchRequestBuilder, ToRpcParams}; // Re-exports for the `rpc_params` macro. #[doc(hidden)] pub mod __reexports { // Needs to be in scope for `ArrayParams` to implement it. - pub use jsonrpsee_types::ToRpcParams; + pub use crate::params::ToRpcParams; // Main builder object for constructing the rpc parameters. - pub use jsonrpsee_types::ArrayParamsBuilder; + pub use crate::params::ArrayParamsBuilder; // Empty rpc parameters for empty macro. - pub use jsonrpsee_types::EmptyParams; + pub use crate::params::EmptyParams; } cfg_async_client! { diff --git a/core/src/lib.rs b/core/src/lib.rs index 2c68b076cc..aba1f50aa2 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -39,6 +39,9 @@ pub mod error; /// Traits pub mod traits; +/// RPC Parameters. +pub mod params; + cfg_http_helpers! { pub mod http_helpers; } diff --git a/core/src/params.rs b/core/src/params.rs new file mode 100644 index 0000000000..1708d7cf6b --- /dev/null +++ b/core/src/params.rs @@ -0,0 +1,299 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! RPC parameters. + +use serde::Serialize; +use serde_json::value::RawValue; + +/// Helper module for building parameters. +mod params_builder { + use serde::Serialize; + + /// Initial number of bytes for a parameter length. + const PARAM_BYTES_CAPACITY: usize = 128; + + /// Generic parameter builder that serializes parameters to bytes. + /// This produces a JSON compatible String. + /// + /// The implementation relies on `Vec` to hold the serialized + /// parameters in memory for the following reasons: + /// 1. Other serialization methods than `serde_json::to_writer` would internally + /// have an extra heap allocation for temporarily holding the value in memory. + /// 2. `io::Write` is not implemented for `String` required for serialization. + #[derive(Debug)] + pub(crate) struct ParamsBuilder { + bytes: Vec, + end: char, + } + + impl ParamsBuilder { + /// Construct a new [`ParamsBuilder`] with custom start and end tokens. + /// The inserted values are wrapped by the _start_ and _end_ tokens. + fn new(start: char, end: char) -> Self { + let mut bytes = Vec::with_capacity(PARAM_BYTES_CAPACITY); + bytes.push(start as u8); + ParamsBuilder { bytes, end } + } + + /// Construct a new [`ParamsBuilder`] for positional parameters equivalent to a JSON array object. + pub(crate) fn positional() -> Self { + Self::new('[', ']') + } + + /// Construct a new [`ParamsBuilder`] for named parameters equivalent to a JSON map object. + pub(crate) fn named() -> Self { + Self::new('{', '}') + } + + /// Insert a named value (key, value) pair into the builder. + /// The _name_ and _value_ are delimited by the `:` token. + pub(crate) fn insert_named(&mut self, name: &str, value: P) -> Result<(), serde_json::Error> { + serde_json::to_writer(&mut self.bytes, name)?; + self.bytes.push(b':'); + serde_json::to_writer(&mut self.bytes, &value)?; + self.bytes.push(b','); + + Ok(()) + } + + /// Insert a plain value into the builder. + pub(crate) fn insert(&mut self, value: P) -> Result<(), serde_json::Error> { + serde_json::to_writer(&mut self.bytes, &value)?; + self.bytes.push(b','); + + Ok(()) + } + + /// Finish the building process and return a JSON compatible string. + pub(crate) fn build(mut self) -> String { + let idx = self.bytes.len() - 1; + if self.bytes[idx] == b',' { + self.bytes[idx] = self.end as u8; + } else { + self.bytes.push(self.end as u8); + } + + // Safety: This is safe because JSON does not emit invalid UTF-8. + unsafe { String::from_utf8_unchecked(self.bytes) } + } + } +} + +/// Parameter builder that serializes named value parameters to a JSON compatible string. +/// This is the equivalent of a JSON Map object `{ key: value }`. +/// +/// # Examples +/// +/// ```rust +/// +/// use jsonrpsee_core::params::ObjectParamsBuilder; +/// +/// let mut builder = ObjectParamsBuilder::new(); +/// builder.insert("param1", 1); +/// builder.insert("param2", "abc"); +/// let params = builder.build(); +/// +/// // Use RPC parameters... +/// ``` +#[derive(Debug)] +pub struct ObjectParamsBuilder(params_builder::ParamsBuilder); + +impl ObjectParamsBuilder { + /// Construct a new [`ObjectParamsBuilder`]. + pub fn new() -> Self { + Self::default() + } + + /// Insert a named value (key, value) pair into the builder. + /// The _name_ and _value_ are delimited by the `:` token. + pub fn insert(&mut self, name: &str, value: P) -> Result<(), serde_json::Error> { + self.0.insert_named(name, value) + } + + /// Finish the building process and return a JSON compatible string. + pub fn build(self) -> ObjectParams { + ObjectParams(self.0.build()) + } +} + +impl Default for ObjectParamsBuilder { + fn default() -> Self { + Self(params_builder::ParamsBuilder::named()) + } +} + +/// Object RPC parameters stored as a JSON Map object `{ key: value }`. +#[derive(Clone, Debug)] +pub struct ObjectParams(String); + +impl ToRpcParams for ObjectParams { + fn to_rpc_params(self) -> Result>, serde_json::Error> { + RawValue::from_string(self.0).map(Some) + } +} + +/// Parameter builder that serializes plain value parameters to a JSON compatible string. +/// This is the equivalent of a JSON Array object `[ value0, value1, .., valueN ]`. +/// +/// # Examples +/// +/// ```rust +/// +/// use jsonrpsee_core::params::ArrayParamsBuilder; +/// +/// let mut builder = ArrayParamsBuilder::new(); +/// builder.insert("param1"); +/// builder.insert(1); +/// let params = builder.build(); +/// +/// // Use RPC parameters... +/// ``` +#[derive(Debug)] +pub struct ArrayParamsBuilder(params_builder::ParamsBuilder); + +impl ArrayParamsBuilder { + /// Construct a new [`ArrayParamsBuilder`]. + pub fn new() -> Self { + Self::default() + } + + /// Insert a plain value into the builder. + pub fn insert(&mut self, value: P) -> Result<(), serde_json::Error> { + self.0.insert(value) + } + + /// Finish the building process and return a JSON compatible string. + pub fn build(self) -> ArrayParams { + ArrayParams(self.0.build()) + } +} + +impl Default for ArrayParamsBuilder { + fn default() -> Self { + Self(params_builder::ParamsBuilder::positional()) + } +} + +/// Array RPC parameters stored as a JSON Array object `[ value0, value1, .., valueN ]`. +#[derive(Clone, Debug)] +pub struct ArrayParams(String); + +impl ToRpcParams for ArrayParams { + fn to_rpc_params(self) -> Result>, serde_json::Error> { + RawValue::from_string(self.0).map(Some) + } +} + +/// Marker trait for types that can be serialized as JSON compatible strings. +/// +/// This trait ensures the correctness of the RPC parameters. +/// +/// # Note +/// +/// Please consider using the [`ArrayParamsBuilder`] and [`ObjectParamsBuilder`] than +/// implementing this trait. +/// +/// # Examples +/// +/// - Implementation for hard-coded strings +/// +/// ```rust +/// +/// use jsonrpsee_core::params::ToRpcParams; +/// use serde_json::value::RawValue; +/// +/// struct ManualParam; +/// +/// impl ToRpcParams for ManualParam { +/// fn to_rpc_params(self) -> Result>, serde_json::Error> { +/// // Manually define a valid JSONRPC parameter. +/// RawValue::from_string("[1, \"2\", 3]".to_string()).map(Some) +/// } +/// } +/// ``` +/// +/// - Implementation for JSON serializable structures +/// +/// ```rust +/// use jsonrpsee_core::params::ToRpcParams; +/// use serde_json::value::RawValue; +/// use serde::Serialize; +/// +/// #[derive(Serialize)] +/// struct SerParam { +/// param_1: u8, +/// param_2: String, +/// }; +/// +/// impl ToRpcParams for SerParam { +/// fn to_rpc_params(self) -> Result>, serde_json::Error> { +/// let s = String::from_utf8(serde_json::to_vec(&self)?).expect("Valid UTF8 format"); +/// RawValue::from_string(s).map(Some) +/// } +/// } +/// ``` +pub trait ToRpcParams { + /// Consume and serialize the type as a JSON raw value. + fn to_rpc_params(self) -> Result>, serde_json::Error>; +} + +/// Initial number of parameters in a batch request. +const BATCH_PARAMS_NUM_CAPACITY: usize = 4; + +/// Request builder that serializes RPC parameters to construct a valid batch parameter. +/// This is the equivalent of chaining multiple RPC requests. +#[derive(Clone, Debug, Default)] +pub struct BatchRequestBuilder<'a>(Vec<(&'a str, Option>)>); + +impl<'a> BatchRequestBuilder<'a> { + /// Construct a new [`BatchRequestBuilder`]. + pub fn new() -> Self { + Self(Vec::with_capacity(BATCH_PARAMS_NUM_CAPACITY)) + } + + /// Inserts the RPC method with provided parameters into the builder. + pub fn insert(&mut self, method: &'a str, value: Params) -> Result<(), serde_json::Error> { + self.0.push((method, value.to_rpc_params()?)); + Ok(()) + } + + /// Finish the building process and return a valid batch parameter. + pub fn build(self) -> Vec<(&'a str, Option>)> { + self.0 + } +} + +/// Empty RPC parameters that perform no allocation. +#[derive(Clone, Debug)] +pub struct EmptyParams; + +/// Custom implementation for empty RPC parameters. +impl ToRpcParams for EmptyParams { + fn to_rpc_params(self) -> Result>, serde_json::Error> { + Ok(None) + } +} diff --git a/proc-macros/src/render_client.rs b/proc-macros/src/render_client.rs index c86bd39fee..6c565ae4a6 100644 --- a/proc-macros/src/render_client.rs +++ b/proc-macros/src/render_client.rs @@ -154,7 +154,7 @@ impl RpcDescription { if params.is_empty() { return quote!({ - #jsonrpsee::types::EmptyParams + #jsonrpsee::core::params::EmptyParams }); } @@ -170,7 +170,7 @@ impl RpcDescription { quote!(#name, #value) }); quote!({ - let mut builder = #jsonrpsee::types::ObjectParamsBuilder::new(); + let mut builder = #jsonrpsee::core::params::ObjectParamsBuilder::new(); #( builder.insert( #params_insert ).expect(format!("Parameters {} must be valid", stringify!(#params_insert)).as_str()); )* builder.build() }) @@ -179,7 +179,7 @@ impl RpcDescription { // Throw away the type. let params = params.iter().map(|(param, _param_type)| param); quote!({ - let mut builder = #jsonrpsee::types::ArrayParamsBuilder::new(); + let mut builder = #jsonrpsee::core::params::ArrayParamsBuilder::new(); #( builder.insert( #params ).expect(format!("Parameters {} must be valid", stringify!(#params)).as_str()); )* builder.build() }) diff --git a/proc-macros/tests/ui/correct/basic.rs b/proc-macros/tests/ui/correct/basic.rs index ff9496db0f..06ecfbe420 100644 --- a/proc-macros/tests/ui/correct/basic.rs +++ b/proc-macros/tests/ui/correct/basic.rs @@ -5,7 +5,8 @@ use std::net::SocketAddr; use jsonrpsee::core::{async_trait, client::ClientT, RpcResult}; use jsonrpsee::proc_macros::rpc; use jsonrpsee::rpc_params; -use jsonrpsee::types::{SubscriptionResult, ArrayParams, EmptyParams}; +use jsonrpsee::core::params::{ArrayParams, EmptyParams}; +use jsonrpsee::types::SubscriptionResult; use jsonrpsee::ws_client::*; use jsonrpsee::ws_server::{SubscriptionSink, WsServerBuilder}; diff --git a/tests/tests/integration_tests.rs b/tests/tests/integration_tests.rs index 1b46d982ec..f32b306e0a 100644 --- a/tests/tests/integration_tests.rs +++ b/tests/tests/integration_tests.rs @@ -35,12 +35,12 @@ use helpers::{http_server, http_server_with_access_control, websocket_server, we use hyper::http::HeaderValue; use jsonrpsee::core::client::{ClientT, IdKind, Subscription, SubscriptionClientT}; use jsonrpsee::core::error::SubscriptionClosed; +use jsonrpsee::core::params::{ArrayParams, BatchRequestBuilder, EmptyParams}; use jsonrpsee::core::{Error, JsonValue}; use jsonrpsee::http_client::HttpClientBuilder; use jsonrpsee::http_server::AccessControlBuilder; use jsonrpsee::rpc_params; use jsonrpsee::types::error::ErrorObject; -use jsonrpsee::types::{ArrayParams, BatchRequestBuilder, EmptyParams}; use jsonrpsee::ws_client::WsClientBuilder; use tokio::time::interval; use tokio_stream::wrappers::IntervalStream; diff --git a/tests/tests/proc_macros.rs b/tests/tests/proc_macros.rs index 41b2f62bd5..b5ede7b8f9 100644 --- a/tests/tests/proc_macros.rs +++ b/tests/tests/proc_macros.rs @@ -196,7 +196,7 @@ mod rpc_impl { } // Use generated implementations of server and client. -use crate::types::{ArrayParams, ObjectParams, ObjectParamsBuilder}; +use jsonrpsee::core::params::{ArrayParams, ObjectParams, ObjectParamsBuilder}; use rpc_impl::{RpcClient, RpcServer, RpcServerImpl}; pub async fn websocket_server() -> SocketAddr { diff --git a/tests/tests/resource_limiting.rs b/tests/tests/resource_limiting.rs index c519153c61..cd0ac9841e 100644 --- a/tests/tests/resource_limiting.rs +++ b/tests/tests/resource_limiting.rs @@ -29,12 +29,13 @@ use std::time::Duration; use futures::StreamExt; use jsonrpsee::core::client::{ClientT, SubscriptionClientT}; +use jsonrpsee::core::params::EmptyParams; use jsonrpsee::core::Error; use jsonrpsee::http_client::HttpClientBuilder; use jsonrpsee::http_server::{HttpServerBuilder, HttpServerHandle}; use jsonrpsee::proc_macros::rpc; use jsonrpsee::types::error::CallError; -use jsonrpsee::types::{EmptyParams, SubscriptionResult}; +use jsonrpsee::types::SubscriptionResult; use jsonrpsee::ws_client::WsClientBuilder; use jsonrpsee::ws_server::{WsServerBuilder, WsServerHandle}; diff --git a/types/src/lib.rs b/types/src/lib.rs index 712daf0456..3a8837f328 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -43,10 +43,7 @@ pub mod response; pub mod error; pub use error::{ErrorObject, ErrorObjectOwned, ErrorResponse, SubscriptionEmptyError, SubscriptionResult}; -pub use params::{ - ArrayParams, ArrayParamsBuilder, BatchRequestBuilder, EmptyParams, Id, ObjectParams, ObjectParamsBuilder, Params, - ParamsSequence, ParamsSer, SubscriptionId, ToRpcParams, TwoPointZero, -}; +pub use params::{Id, Params, ParamsSequence, ParamsSer, SubscriptionId, TwoPointZero}; pub use request::{InvalidRequest, Notification, NotificationSer, Request, RequestSer}; pub use response::{Response, SubscriptionPayload, SubscriptionResponse}; diff --git a/types/src/params.rs b/types/src/params.rs index 29e9dc1446..5c015ef417 100644 --- a/types/src/params.rs +++ b/types/src/params.rs @@ -36,16 +36,8 @@ use beef::Cow; use serde::de::{self, Deserializer, Unexpected, Visitor}; use serde::ser::Serializer; use serde::{Deserialize, Serialize}; -use serde_json::value::RawValue; use serde_json::Value as JsonValue; -#[doc(hidden)] -pub mod __reexports { - pub use crate::params::ArrayParams; - pub use crate::params::ArrayParamsBuilder; - pub use crate::params::ToRpcParams; -} - /// JSON-RPC v2 marker type. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct TwoPointZero; @@ -416,275 +408,6 @@ impl<'a> Id<'a> { } } -/// Helper module for building parameters. -mod params_builder { - use serde::Serialize; - - /// Initial number of bytes for a parameter length. - const PARAM_BYTES_CAPACITY: usize = 128; - - /// Generic parameter builder that serializes parameters to bytes. - /// This produces a JSON compatible String. - /// - /// The implementation relies on `Vec` to hold the serialized - /// parameters in memory for the following reasons: - /// 1. Other serialization methods than `serde_json::to_writer` would internally - /// have an extra heap allocation for temporarily holding the value in memory. - /// 2. `io::Write` is not implemented for `String` required for serialization. - #[derive(Debug)] - pub struct ParamsBuilder { - bytes: Vec, - end: char, - } - - impl ParamsBuilder { - /// Construct a new [`ParamsBuilder`] with custom start and end tokens. - /// The inserted values are wrapped by the _start_ and _end_ tokens. - fn new(start: char, end: char) -> Self { - let mut bytes = Vec::with_capacity(PARAM_BYTES_CAPACITY); - bytes.push(start as u8); - ParamsBuilder { bytes, end } - } - - /// Construct a new [`ParamsBuilder`] for positional parameters equivalent to a JSON array object. - pub fn positional() -> Self { - Self::new('[', ']') - } - - /// Construct a new [`ParamsBuilder`] for named parameters equivalent to a JSON map object. - pub fn named() -> Self { - Self::new('{', '}') - } - - /// Insert a named value (key, value) pair into the builder. - /// The _name_ and _value_ are delimited by the `:` token. - pub fn insert_named(&mut self, name: &str, value: P) -> Result<(), serde_json::Error> { - serde_json::to_writer(&mut self.bytes, name)?; - self.bytes.push(b':'); - serde_json::to_writer(&mut self.bytes, &value)?; - self.bytes.push(b','); - - Ok(()) - } - - /// Insert a plain value into the builder. - pub fn insert(&mut self, value: P) -> Result<(), serde_json::Error> { - serde_json::to_writer(&mut self.bytes, &value)?; - self.bytes.push(b','); - - Ok(()) - } - - /// Finish the building process and return a JSON compatible string. - pub fn build(mut self) -> String { - let idx = self.bytes.len() - 1; - if self.bytes[idx] == b',' { - self.bytes[idx] = self.end as u8; - } else { - self.bytes.push(self.end as u8); - } - - // Safety: This is safe because JSON does not emit invalid UTF-8. - unsafe { String::from_utf8_unchecked(self.bytes) } - } - } -} - -/// Parameter builder that serializes named value parameters to a JSON compatible string. -/// This is the equivalent of a JSON Map object `{ key: value }`. -/// -/// # Examples -/// -/// ```rust -/// -/// use jsonrpsee_types::ObjectParamsBuilder; -/// -/// let mut builder = ObjectParamsBuilder::new(); -/// builder.insert("param1", 1); -/// builder.insert("param2", "abc"); -/// let params = builder.build(); -/// -/// // Use RPC parameters... -/// ``` -#[derive(Debug)] -pub struct ObjectParamsBuilder(params_builder::ParamsBuilder); - -impl ObjectParamsBuilder { - /// Construct a new [`ObjectParamsBuilder`]. - pub fn new() -> Self { - Self::default() - } - - /// Insert a named value (key, value) pair into the builder. - /// The _name_ and _value_ are delimited by the `:` token. - pub fn insert(&mut self, name: &str, value: P) -> Result<(), serde_json::Error> { - self.0.insert_named(name, value) - } - - /// Finish the building process and return a JSON compatible string. - pub fn build(self) -> ObjectParams { - ObjectParams(self.0.build()) - } -} - -impl Default for ObjectParamsBuilder { - fn default() -> Self { - Self(params_builder::ParamsBuilder::named()) - } -} - -/// Object RPC parameters stored as a JSON Map object `{ key: value }`. -#[derive(Clone, Debug)] -pub struct ObjectParams(String); - -impl ToRpcParams for ObjectParams { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - RawValue::from_string(self.0).map(Some) - } -} - -/// Parameter builder that serializes plain value parameters to a JSON compatible string. -/// This is the equivalent of a JSON Array object `[ value0, value1, .., valueN ]`. -/// -/// # Examples -/// -/// ```rust -/// -/// use jsonrpsee_types::ArrayParamsBuilder; -/// -/// let mut builder = ArrayParamsBuilder::new(); -/// builder.insert("param1"); -/// builder.insert(1); -/// let params = builder.build(); -/// -/// // Use RPC parameters... -/// ``` -#[derive(Debug)] -pub struct ArrayParamsBuilder(params_builder::ParamsBuilder); - -impl ArrayParamsBuilder { - /// Construct a new [`ArrayParamsBuilder`]. - pub fn new() -> Self { - Self::default() - } - - /// Insert a plain value into the builder. - pub fn insert(&mut self, value: P) -> Result<(), serde_json::Error> { - self.0.insert(value) - } - - /// Finish the building process and return a JSON compatible string. - pub fn build(self) -> ArrayParams { - ArrayParams(self.0.build()) - } -} - -impl Default for ArrayParamsBuilder { - fn default() -> Self { - Self(params_builder::ParamsBuilder::positional()) - } -} - -/// Array RPC parameters stored as a JSON Array object `[ value0, value1, .., valueN ]`. -#[derive(Clone, Debug)] -pub struct ArrayParams(String); - -impl ToRpcParams for ArrayParams { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - RawValue::from_string(self.0).map(Some) - } -} - -/// Marker trait for types that can be serialized as JSON compatible strings. -/// -/// This trait ensures the correctness of the RPC parameters. -/// -/// # Note -/// -/// Please consider using the [`ArrayParamsBuilder`] and [`ObjectParamsBuilder`] than -/// implementing this trait. -/// -/// # Examples -/// -/// - Implementation for hard-coded strings -/// -/// ```rust -/// -/// use jsonrpsee_types::ToRpcParams; -/// use serde_json::value::RawValue; -/// -/// struct ManualParam; -/// -/// impl ToRpcParams for ManualParam { -/// fn to_rpc_params(self) -> Result>, serde_json::Error> { -/// // Manually define a valid JSONRPC parameter. -/// RawValue::from_string("[1, \"2\", 3]".to_string()).map(Some) -/// } -/// } -/// ``` -/// -/// - Implementation for JSON serializable structures -/// -/// ```rust -/// use jsonrpsee_types::ToRpcParams; -/// use serde_json::value::RawValue; -/// use serde::Serialize; -/// -/// #[derive(Serialize)] -/// struct SerParam { -/// param_1: u8, -/// param_2: String, -/// }; -/// -/// impl ToRpcParams for SerParam { -/// fn to_rpc_params(self) -> Result>, serde_json::Error> { -/// let s = String::from_utf8(serde_json::to_vec(&self)?).expect("Valid UTF8 format"); -/// RawValue::from_string(s).map(Some) -/// } -/// } -/// ``` -pub trait ToRpcParams { - /// Consume and serialize the type as a JSON raw value. - fn to_rpc_params(self) -> Result>, serde_json::Error>; -} - -/// Initial number of parameters in a batch request. -const BATCH_PARAMS_NUM_CAPACITY: usize = 4; - -/// Request builder that serializes RPC parameters to construct a valid batch parameter. -/// This is the equivalent of chaining multiple RPC requests. -#[derive(Clone, Debug, Default)] -pub struct BatchRequestBuilder<'a>(Vec<(&'a str, Option>)>); - -impl<'a> BatchRequestBuilder<'a> { - /// Construct a new [`BatchRequestBuilder`]. - pub fn new() -> Self { - Self(Vec::with_capacity(BATCH_PARAMS_NUM_CAPACITY)) - } - - /// Inserts the RPC method with provided parameters into the builder. - pub fn insert(&mut self, method: &'a str, value: Params) -> Result<(), serde_json::Error> { - self.0.push((method, value.to_rpc_params()?)); - Ok(()) - } - - /// Finish the building process and return a valid batch parameter. - pub fn build(self) -> Vec<(&'a str, Option>)> { - self.0 - } -} - -/// Empty RPC parameters that perform no allocation. -#[derive(Clone, Debug)] -pub struct EmptyParams; - -/// Custom implementation for empty RPC parameters. -impl ToRpcParams for EmptyParams { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - Ok(None) - } -} - #[cfg(test)] mod test { use super::{Cow, Id, JsonValue, Params, ParamsSer, SubscriptionId, TwoPointZero}; diff --git a/types/src/request.rs b/types/src/request.rs index f5a0210aef..e20d4ef632 100644 --- a/types/src/request.rs +++ b/types/src/request.rs @@ -130,7 +130,6 @@ impl<'a> NotificationSer<'a> { #[cfg(test)] mod test { use super::{Id, InvalidRequest, Notification, NotificationSer, Request, RequestSer, TwoPointZero}; - use crate::{ArrayParamsBuilder, EmptyParams, ToRpcParams}; use serde_json::value::RawValue; fn assert_request<'a>(request: Request<'a>, id: Id<'a>, method: &str, params: Option<&str>) { @@ -207,27 +206,19 @@ mod test { fn serialize_call() { let method = "subtract"; let id = Id::Number(1); // It's enough to check one variant, since the type itself also has tests. - let mut builder = ArrayParamsBuilder::new(); - builder.insert(42).unwrap(); - builder.insert(23).unwrap(); - let params = builder.build().to_rpc_params().unwrap(); // Same as above. + let params = Some(RawValue::from_string("[42,23]".into()).unwrap()); let test_vector: &[(&'static str, Option<_>, Option<_>, &'static str)] = &[ // With all fields set. (r#"{"jsonrpc":"2.0","id":1,"method":"subtract","params":[42,23]}"#, Some(&id), params.clone(), method), // Escaped method name. - (r#"{"jsonrpc":"2.0","id":1,"method":"\"m"}"#, Some(&id), EmptyParams.to_rpc_params().unwrap(), "\"m"), + (r#"{"jsonrpc":"2.0","id":1,"method":"\"m"}"#, Some(&id), None, "\"m"), // Without ID field. (r#"{"jsonrpc":"2.0","id":null,"method":"subtract","params":[42,23]}"#, None, params, method), // Without params field - ( - r#"{"jsonrpc":"2.0","id":1,"method":"subtract"}"#, - Some(&id), - EmptyParams.to_rpc_params().unwrap(), - method, - ), + (r#"{"jsonrpc":"2.0","id":1,"method":"subtract"}"#, Some(&id), None, method), // Without params and ID. - (r#"{"jsonrpc":"2.0","id":null,"method":"subtract"}"#, None, EmptyParams.to_rpc_params().unwrap(), method), + (r#"{"jsonrpc":"2.0","id":null,"method":"subtract"}"#, None, None, method), ]; for (ser, id, params, method) in test_vector.iter().cloned() { @@ -246,9 +237,7 @@ mod test { #[test] fn serialize_notif() { let exp = r#"{"jsonrpc":"2.0","method":"say_hello","params":["hello"]}"#; - let mut builder = ArrayParamsBuilder::new(); - builder.insert("hello").unwrap(); - let params = builder.build().to_rpc_params().unwrap(); + let params = Some(RawValue::from_string(r#"["hello"]"#.into()).unwrap()); let req = NotificationSer::new("say_hello", params); let ser = serde_json::to_string(&req).unwrap(); assert_eq!(exp, ser); From 22f31dcb2e06be86dcae65680c76e1bb24f9fc87 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 31 Aug 2022 18:39:19 +0300 Subject: [PATCH 28/41] core: Return `core::Error` from `ToRpcParams` trait Signed-off-by: Alexandru Vasile --- client/http-client/src/tests.rs | 4 ++-- client/ws-client/src/tests.rs | 4 ++-- core/src/params.rs | 25 ++++++++++++++----------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/client/http-client/src/tests.rs b/client/http-client/src/tests.rs index 83dea9bc9f..c6d6b61925 100644 --- a/client/http-client/src/tests.rs +++ b/client/http-client/src/tests.rs @@ -158,8 +158,8 @@ async fn batch_request_out_of_order_response() { assert_eq!(response, vec!["hello".to_string(), "goodbye".to_string(), "here's your swag".to_string()]); } -async fn run_batch_request_with_response<'a>( - batch: BatchRequestBuilder<'a>, +async fn run_batch_request_with_response( + batch: BatchRequestBuilder<'_>, response: String, ) -> Result, Error> { let server_addr = http_server_with_hardcoded_response(response).with_default_timeout().await.unwrap(); diff --git a/client/ws-client/src/tests.rs b/client/ws-client/src/tests.rs index f1cd882d71..641051b784 100644 --- a/client/ws-client/src/tests.rs +++ b/client/ws-client/src/tests.rs @@ -274,8 +274,8 @@ async fn is_connected_works() { assert!(!client.is_connected()) } -async fn run_batch_request_with_response<'a>( - batch: BatchRequestBuilder<'a>, +async fn run_batch_request_with_response( + batch: BatchRequestBuilder<'_>, response: String, ) -> Result, Error> { let server = WebSocketTestServer::with_hardcoded_response("127.0.0.1:0".parse().unwrap(), response) diff --git a/core/src/params.rs b/core/src/params.rs index 1708d7cf6b..1b8d383997 100644 --- a/core/src/params.rs +++ b/core/src/params.rs @@ -26,6 +26,7 @@ //! RPC parameters. +use crate::Error; use serde::Serialize; use serde_json::value::RawValue; @@ -151,8 +152,8 @@ impl Default for ObjectParamsBuilder { pub struct ObjectParams(String); impl ToRpcParams for ObjectParams { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - RawValue::from_string(self.0).map(Some) + fn to_rpc_params(self) -> Result>, Error> { + RawValue::from_string(self.0).map(Some).map_err(Error::ParseError) } } @@ -203,8 +204,8 @@ impl Default for ArrayParamsBuilder { pub struct ArrayParams(String); impl ToRpcParams for ArrayParams { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - RawValue::from_string(self.0).map(Some) + fn to_rpc_params(self) -> Result>, Error> { + RawValue::from_string(self.0).map(Some).map_err(Error::ParseError) } } @@ -225,13 +226,14 @@ impl ToRpcParams for ArrayParams { /// /// use jsonrpsee_core::params::ToRpcParams; /// use serde_json::value::RawValue; +/// use jsonrpsee_core::Error; /// /// struct ManualParam; /// /// impl ToRpcParams for ManualParam { -/// fn to_rpc_params(self) -> Result>, serde_json::Error> { +/// fn to_rpc_params(self) -> Result>, Error> { /// // Manually define a valid JSONRPC parameter. -/// RawValue::from_string("[1, \"2\", 3]".to_string()).map(Some) +/// RawValue::from_string("[1, \"2\", 3]".to_string()).map(Some).map_err(Error::ParseError) /// } /// } /// ``` @@ -242,6 +244,7 @@ impl ToRpcParams for ArrayParams { /// use jsonrpsee_core::params::ToRpcParams; /// use serde_json::value::RawValue; /// use serde::Serialize; +/// use jsonrpsee_core::Error; /// /// #[derive(Serialize)] /// struct SerParam { @@ -250,15 +253,15 @@ impl ToRpcParams for ArrayParams { /// }; /// /// impl ToRpcParams for SerParam { -/// fn to_rpc_params(self) -> Result>, serde_json::Error> { +/// fn to_rpc_params(self) -> Result>, Error> { /// let s = String::from_utf8(serde_json::to_vec(&self)?).expect("Valid UTF8 format"); -/// RawValue::from_string(s).map(Some) +/// RawValue::from_string(s).map(Some).map_err(Error::ParseError) /// } /// } /// ``` pub trait ToRpcParams { /// Consume and serialize the type as a JSON raw value. - fn to_rpc_params(self) -> Result>, serde_json::Error>; + fn to_rpc_params(self) -> Result>, Error>; } /// Initial number of parameters in a batch request. @@ -276,7 +279,7 @@ impl<'a> BatchRequestBuilder<'a> { } /// Inserts the RPC method with provided parameters into the builder. - pub fn insert(&mut self, method: &'a str, value: Params) -> Result<(), serde_json::Error> { + pub fn insert(&mut self, method: &'a str, value: Params) -> Result<(), Error> { self.0.push((method, value.to_rpc_params()?)); Ok(()) } @@ -293,7 +296,7 @@ pub struct EmptyParams; /// Custom implementation for empty RPC parameters. impl ToRpcParams for EmptyParams { - fn to_rpc_params(self) -> Result>, serde_json::Error> { + fn to_rpc_params(self) -> Result>, Error> { Ok(None) } } From 0129bec0341491cec7c1f37a35673bd2f9c7c3bb Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Wed, 31 Aug 2022 18:49:00 +0300 Subject: [PATCH 29/41] Fix doc link Signed-off-by: Alexandru Vasile --- core/src/client/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index 629feade43..4e2ccc1041 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -182,7 +182,7 @@ pub trait TransportReceiverT: 'static { async fn receive(&mut self) -> Result; } -/// Convert the given values to a [`jsonrpsee_types::ArrayParamsBuilder`], or empty tuple as expected by a +/// Convert the given values to a [`jsonrpsee::core::params::ArrayParamsBuilder`], or empty tuple as expected by a /// jsonrpsee Client (http or websocket). /// /// # Panics From 0d2ccd80240252bef533c130bcb27c0dbee1a22e Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Thu, 1 Sep 2022 11:52:45 +0300 Subject: [PATCH 30/41] Fix `ArrayParamsBuilder` doc links Signed-off-by: Alexandru Vasile --- core/src/client/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index 4e2ccc1041..d6966dcae1 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -182,7 +182,7 @@ pub trait TransportReceiverT: 'static { async fn receive(&mut self) -> Result; } -/// Convert the given values to a [`jsonrpsee::core::params::ArrayParamsBuilder`], or empty tuple as expected by a +/// Convert the given values to a [`crate::params::ArrayParamsBuilder`], or empty tuple as expected by a /// jsonrpsee Client (http or websocket). /// /// # Panics From 126c27a36d992a5555336e863341cb4be62a6f79 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Thu, 1 Sep 2022 12:28:04 +0300 Subject: [PATCH 31/41] Remove `ToRpcServerParams` trait Signed-off-by: Alexandru Vasile --- benches/bench.rs | 3 +- client/http-client/src/client.rs | 3 +- core/src/client/async_client/helpers.rs | 3 +- core/src/client/async_client/mod.rs | 3 +- core/src/client/mod.rs | 5 +- core/src/params.rs | 56 +-------------- core/src/server/rpc_module.rs | 14 ++-- core/src/traits.rs | 93 +++++++++++++++++++++---- 8 files changed, 99 insertions(+), 81 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index 58ab928914..6efa870887 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -6,7 +6,8 @@ use futures_util::future::{join_all, FutureExt}; use futures_util::stream::FuturesUnordered; use helpers::{http_client, ws_client, SUB_METHOD_NAME, UNSUB_METHOD_NAME}; use jsonrpsee::core::client::{ClientT, SubscriptionClientT}; -use jsonrpsee::core::params::{ArrayParamsBuilder, BatchRequestBuilder, EmptyParams, ToRpcParams}; +use jsonrpsee::core::params::{ArrayParamsBuilder, BatchRequestBuilder, EmptyParams}; +use jsonrpsee::core::traits::ToRpcParams; use jsonrpsee::http_client::HeaderMap; use jsonrpsee::types::{Id, ParamsSer, RequestSer}; use pprof::criterion::{Output, PProfProfiler}; diff --git a/client/http-client/src/client.rs b/client/http-client/src/client.rs index 792c0cc1cd..d7feb54176 100644 --- a/client/http-client/src/client.rs +++ b/client/http-client/src/client.rs @@ -32,8 +32,9 @@ use crate::types::{ErrorResponse, Id, NotificationSer, RequestSer, Response}; use async_trait::async_trait; use hyper::http::HeaderMap; use jsonrpsee_core::client::{CertificateStore, ClientT, IdKind, RequestIdManager, Subscription, SubscriptionClientT}; -use jsonrpsee_core::params::{BatchRequestBuilder, ToRpcParams}; +use jsonrpsee_core::params::BatchRequestBuilder; use jsonrpsee_core::tracing::RpcTracing; +use jsonrpsee_core::traits::ToRpcParams; use jsonrpsee_core::{Error, TEN_MB_SIZE_BYTES}; use jsonrpsee_types::error::CallError; use rustc_hash::FxHashMap; diff --git a/core/src/client/async_client/helpers.rs b/core/src/client/async_client/helpers.rs index 0a5b841fd8..151b144e2f 100644 --- a/core/src/client/async_client/helpers.rs +++ b/core/src/client/async_client/helpers.rs @@ -36,7 +36,8 @@ use jsonrpsee_types::error::CallError; use jsonrpsee_types::response::SubscriptionError; use jsonrpsee_types::{ErrorResponse, Id, Notification, RequestSer, Response, SubscriptionId, SubscriptionResponse}; use serde_json::Value as JsonValue; -use crate::params::{ArrayParamsBuilder, ToRpcParams}; +use crate::params::ArrayParamsBuilder; +use crate::traits::ToRpcParams; /// Attempts to process a batch response. /// diff --git a/core/src/client/async_client/mod.rs b/core/src/client/async_client/mod.rs index 5d494d45cb..40935a4a19 100644 --- a/core/src/client/async_client/mod.rs +++ b/core/src/client/async_client/mod.rs @@ -32,7 +32,8 @@ use jsonrpsee_types::{ }; use serde::de::DeserializeOwned; use tracing_futures::Instrument; -use crate::params::{BatchRequestBuilder, ToRpcParams}; +use crate::params::BatchRequestBuilder; +use crate::traits::ToRpcParams; use super::{FrontToBack, IdKind, RequestIdManager}; diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index d6966dcae1..07929e8bac 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -41,13 +41,14 @@ use futures_util::stream::{Stream, StreamExt}; use jsonrpsee_types::{Id, SubscriptionId}; use serde::de::DeserializeOwned; use serde_json::Value as JsonValue; -use crate::params::{BatchRequestBuilder, ToRpcParams}; +use crate::params::BatchRequestBuilder; +use crate::traits::ToRpcParams; // Re-exports for the `rpc_params` macro. #[doc(hidden)] pub mod __reexports { // Needs to be in scope for `ArrayParams` to implement it. - pub use crate::params::ToRpcParams; + pub use crate::traits::ToRpcParams; // Main builder object for constructing the rpc parameters. pub use crate::params::ArrayParamsBuilder; // Empty rpc parameters for empty macro. diff --git a/core/src/params.rs b/core/src/params.rs index 1b8d383997..6b39be84a7 100644 --- a/core/src/params.rs +++ b/core/src/params.rs @@ -26,6 +26,7 @@ //! RPC parameters. +use crate::traits::ToRpcParams; use crate::Error; use serde::Serialize; use serde_json::value::RawValue; @@ -209,61 +210,6 @@ impl ToRpcParams for ArrayParams { } } -/// Marker trait for types that can be serialized as JSON compatible strings. -/// -/// This trait ensures the correctness of the RPC parameters. -/// -/// # Note -/// -/// Please consider using the [`ArrayParamsBuilder`] and [`ObjectParamsBuilder`] than -/// implementing this trait. -/// -/// # Examples -/// -/// - Implementation for hard-coded strings -/// -/// ```rust -/// -/// use jsonrpsee_core::params::ToRpcParams; -/// use serde_json::value::RawValue; -/// use jsonrpsee_core::Error; -/// -/// struct ManualParam; -/// -/// impl ToRpcParams for ManualParam { -/// fn to_rpc_params(self) -> Result>, Error> { -/// // Manually define a valid JSONRPC parameter. -/// RawValue::from_string("[1, \"2\", 3]".to_string()).map(Some).map_err(Error::ParseError) -/// } -/// } -/// ``` -/// -/// - Implementation for JSON serializable structures -/// -/// ```rust -/// use jsonrpsee_core::params::ToRpcParams; -/// use serde_json::value::RawValue; -/// use serde::Serialize; -/// use jsonrpsee_core::Error; -/// -/// #[derive(Serialize)] -/// struct SerParam { -/// param_1: u8, -/// param_2: String, -/// }; -/// -/// impl ToRpcParams for SerParam { -/// fn to_rpc_params(self) -> Result>, Error> { -/// let s = String::from_utf8(serde_json::to_vec(&self)?).expect("Valid UTF8 format"); -/// RawValue::from_string(s).map(Some).map_err(Error::ParseError) -/// } -/// } -/// ``` -pub trait ToRpcParams { - /// Consume and serialize the type as a JSON raw value. - fn to_rpc_params(self) -> Result>, Error>; -} - /// Initial number of parameters in a batch request. const BATCH_PARAMS_NUM_CAPACITY: usize = 4; diff --git a/core/src/server/rpc_module.rs b/core/src/server/rpc_module.rs index fe714b7a28..c077217435 100644 --- a/core/src/server/rpc_module.rs +++ b/core/src/server/rpc_module.rs @@ -34,7 +34,7 @@ use crate::error::{Error, SubscriptionClosed}; use crate::id_providers::RandomIntegerIdProvider; use crate::server::helpers::{BoundedSubscriptions, MethodSink, SubscriptionPermit}; use crate::server::resource_limiting::{ResourceGuard, ResourceTable, ResourceVec, Resources}; -use crate::traits::{IdProvider, ToRpcServerParams}; +use crate::traits::{IdProvider, ToRpcParams}; use futures_channel::{mpsc, oneshot}; use futures_util::future::Either; use futures_util::pin_mut; @@ -337,7 +337,7 @@ impl Methods { /// Helper to call a method on the `RPC module` without having to spin up a server. /// - /// The params must be serializable as JSON array, see [`ToRpcServerParams`] for further documentation. + /// The params must be serializable as JSON array, see [`ToRpcParams`] for further documentation. /// /// Returns the decoded value of the `result field` in JSON-RPC response if successful. /// @@ -357,13 +357,13 @@ impl Methods { /// assert_eq!(echo, 1); /// } /// ``` - pub async fn call( + pub async fn call( &self, method: &str, params: Params, ) -> Result { let params = params.to_rpc_params()?; - let req = Request::new(method.into(), Some(¶ms), Id::Number(0)); + let req = Request::new(method.into(), params.as_ref().map(|p| p.as_ref()), Id::Number(0)); tracing::trace!("[Methods::call] Method: {:?}, params: {:?}", method, params); let (resp, _, _) = self.inner_call(req).await; @@ -450,7 +450,7 @@ impl Methods { /// Helper to create a subscription on the `RPC module` without having to spin up a server. /// - /// The params must be serializable as JSON array, see [`ToRpcServerParams`] for further documentation. + /// The params must be serializable as JSON array, see [`ToRpcParams`] for further documentation. /// /// Returns [`Subscription`] on success which can used to get results from the subscriptions. /// @@ -473,9 +473,9 @@ impl Methods { /// assert_eq!(&sub_resp, "one answer"); /// } /// ``` - pub async fn subscribe(&self, sub_method: &str, params: impl ToRpcServerParams) -> Result { + pub async fn subscribe(&self, sub_method: &str, params: impl ToRpcParams) -> Result { let params = params.to_rpc_params()?; - let req = Request::new(sub_method.into(), Some(¶ms), Id::Number(0)); + let req = Request::new(sub_method.into(), params.as_ref().map(|p| p.as_ref()), Id::Number(0)); tracing::trace!("[Methods::subscribe] Method: {}, params: {:?}", sub_method, params); diff --git a/core/src/traits.rs b/core/src/traits.rs index b333edb062..5097417551 100644 --- a/core/src/traits.rs +++ b/core/src/traits.rs @@ -24,30 +24,97 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +use crate::Error; use jsonrpsee_types::SubscriptionId; use serde::Serialize; use serde_json::value::RawValue; -/// Marker trait for types that can be serialized as JSON array/sequence, -/// as part of the serialization done by the server. +/// Marker trait for types that can be serialized as JSON compatible strings. /// -/// If your type isn't a sequence, for example `String`, `usize` or similar -/// you must insert it in a tuple, slice, array or Vec for it to work. -pub trait ToRpcServerParams: Serialize { - /// Serialize the type as a JSON array. - fn to_rpc_params(&self) -> Result, serde_json::Error> { - serde_json::to_string(&self).map(|json| RawValue::from_string(json).expect("JSON String; qed")) - } +/// This trait ensures the correctness of the RPC parameters. +/// +/// # Note +/// +/// Please consider using the [`crate::params::ArrayParamsBuilder`] and [`crate::params::ObjectParamsBuilder`] than +/// implementing this trait. +/// +/// # Examples +/// +/// - Implementation for hard-coded strings +/// +/// ```rust +/// +/// use jsonrpsee_core::params::ToRpcParams; +/// use serde_json::value::RawValue; +/// use jsonrpsee_core::Error; +/// +/// struct ManualParam; +/// +/// impl ToRpcParams for ManualParam { +/// fn to_rpc_params(self) -> Result>, Error> { +/// // Manually define a valid JSONRPC parameter. +/// RawValue::from_string("[1, \"2\", 3]".to_string()).map(Some).map_err(Error::ParseError) +/// } +/// } +/// ``` +/// +/// - Implementation for JSON serializable structures +/// +/// ```rust +/// use jsonrpsee_core::params::ToRpcParams; +/// use serde_json::value::RawValue; +/// use serde::Serialize; +/// use jsonrpsee_core::Error; +/// +/// #[derive(Serialize)] +/// struct SerParam { +/// param_1: u8, +/// param_2: String, +/// }; +/// +/// impl ToRpcParams for SerParam { +/// fn to_rpc_params(self) -> Result>, Error> { +/// let s = String::from_utf8(serde_json::to_vec(&self)?).expect("Valid UTF8 format"); +/// RawValue::from_string(s).map(Some).map_err(Error::ParseError) +/// } +/// } +/// ``` +pub trait ToRpcParams { + /// Consume and serialize the type as a JSON raw value. + fn to_rpc_params(self) -> Result>, Error>; +} + +// To not bound the `ToRpcParams: Serialize` define a custom implementation +// for types which are serializable. +macro_rules! to_rpc_params_impl { + () => { + fn to_rpc_params(self) -> Result>, Error> { + let json = serde_json::to_string(&self).map_err(Error::ParseError)?; + RawValue::from_string(json).map(Some).map_err(Error::ParseError) + } + }; } -impl ToRpcServerParams for &[P] {} -impl ToRpcServerParams for Vec

{} -impl ToRpcServerParams for [P; N] where [P; N]: Serialize {} +impl ToRpcParams for &[P] { + to_rpc_params_impl!(); +} + +impl ToRpcParams for Vec

{ + to_rpc_params_impl!(); +} +impl ToRpcParams for [P; N] +where + [P; N]: Serialize, +{ + to_rpc_params_impl!(); +} macro_rules! tuple_impls { ($($len:expr => ($($n:tt $name:ident)+))+) => { $( - impl<$($name: Serialize),+> ToRpcServerParams for ($($name,)+) {} + impl<$($name: Serialize),+> ToRpcParams for ($($name,)+) { + to_rpc_params_impl!(); + } )+ } } From a9a582d3af1f7045758a3c12ad944eb8eb5757d2 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 2 Sep 2022 12:26:26 +0300 Subject: [PATCH 32/41] core: Fix `ToRpcParams` docs Signed-off-by: Alexandru Vasile --- core/src/traits.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/traits.rs b/core/src/traits.rs index 5097417551..6265ab8de9 100644 --- a/core/src/traits.rs +++ b/core/src/traits.rs @@ -44,7 +44,7 @@ use serde_json::value::RawValue; /// /// ```rust /// -/// use jsonrpsee_core::params::ToRpcParams; +/// use jsonrpsee_core::traits::ToRpcParams; /// use serde_json::value::RawValue; /// use jsonrpsee_core::Error; /// @@ -61,7 +61,7 @@ use serde_json::value::RawValue; /// - Implementation for JSON serializable structures /// /// ```rust -/// use jsonrpsee_core::params::ToRpcParams; +/// use jsonrpsee_core::traits::ToRpcParams; /// use serde_json::value::RawValue; /// use serde::Serialize; /// use jsonrpsee_core::Error; From 83d94c60ab13e3f9e4dce49342aa1cab7789ced4 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 2 Sep 2022 14:38:57 +0300 Subject: [PATCH 33/41] Remove `ParamsSer` and extend benchmarking Signed-off-by: Alexandru Vasile --- benches/bench.rs | 36 +++++++++++++++++++++--------- types/src/lib.rs | 2 +- types/src/params.rs | 54 +-------------------------------------------- 3 files changed, 28 insertions(+), 64 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index 6efa870887..4bb8a91665 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -6,10 +6,10 @@ use futures_util::future::{join_all, FutureExt}; use futures_util::stream::FuturesUnordered; use helpers::{http_client, ws_client, SUB_METHOD_NAME, UNSUB_METHOD_NAME}; use jsonrpsee::core::client::{ClientT, SubscriptionClientT}; -use jsonrpsee::core::params::{ArrayParamsBuilder, BatchRequestBuilder, EmptyParams}; +use jsonrpsee::core::params::{ArrayParamsBuilder, BatchRequestBuilder, EmptyParams, ObjectParamsBuilder}; use jsonrpsee::core::traits::ToRpcParams; use jsonrpsee::http_client::HeaderMap; -use jsonrpsee::types::{Id, ParamsSer, RequestSer}; +use jsonrpsee::types::{Id, RequestSer}; use pprof::criterion::{Output, PProfProfiler}; use tokio::runtime::Runtime as TokioRuntime; @@ -65,21 +65,17 @@ fn v2_serialize(req: RequestSer<'_>) -> String { } pub fn jsonrpsee_types_v2(crit: &mut Criterion) { - // Construct the serialized request using the `ParamsSer` directly. - crit.bench_function("jsonrpsee_types_baseline_params", |b| { + // Construct the serialized array request using the `RawValue` directly. + crit.bench_function("jsonrpsee_types_array_params_baseline", |b| { b.iter(|| { - let params = &[1_u64.into(), 2_u32.into()]; - let params = ParamsSer::ArrayRef(params); - let params = serde_json::to_string(¶ms).unwrap(); - let params = serde_json::value::RawValue::from_string(params).unwrap(); + let params = serde_json::value::RawValue::from_string("[1, 2]".to_string()).unwrap(); let request = RequestSer::new(&Id::Number(0), "say_hello", Some(params)); v2_serialize(request); }) }); - // Construct the serialized request using the `ArrayParamsBuilder`. - crit.bench_function("jsonrpsee_types_unnamed_params", |b| { + crit.bench_function("jsonrpsee_types_array_params", |b| { b.iter(|| { let mut builder = ArrayParamsBuilder::new(); builder.insert(1u64).unwrap(); @@ -89,6 +85,26 @@ pub fn jsonrpsee_types_v2(crit: &mut Criterion) { v2_serialize(request); }) }); + + // Construct the serialized object request using the `RawValue` directly. + crit.bench_function("jsonrpsee_types_object_params_baseline", |b| { + b.iter(|| { + let params = serde_json::value::RawValue::from_string(r#"{"key": 1}"#.to_string()).unwrap(); + + let request = RequestSer::new(&Id::Number(0), "say_hello", Some(params)); + v2_serialize(request); + }) + }); + // Construct the serialized request using the `ObjectParamsBuilder`. + crit.bench_function("jsonrpsee_types_object_params", |b| { + b.iter(|| { + let mut builder = ObjectParamsBuilder::new(); + builder.insert("key", 1u32).unwrap(); + let params = builder.build().to_rpc_params().expect("Valid params"); + let request = RequestSer::new(&Id::Number(0), "say_hello", params); + v2_serialize(request); + }) + }); } trait RequestBencher { diff --git a/types/src/lib.rs b/types/src/lib.rs index 3a8837f328..9380d06c27 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -43,7 +43,7 @@ pub mod response; pub mod error; pub use error::{ErrorObject, ErrorObjectOwned, ErrorResponse, SubscriptionEmptyError, SubscriptionResult}; -pub use params::{Id, Params, ParamsSequence, ParamsSer, SubscriptionId, TwoPointZero}; +pub use params::{Id, Params, ParamsSequence, SubscriptionId, TwoPointZero}; pub use request::{InvalidRequest, Notification, NotificationSer, Request, RequestSer}; pub use response::{Response, SubscriptionPayload, SubscriptionResponse}; diff --git a/types/src/params.rs b/types/src/params.rs index 5c015ef417..b660182b12 100644 --- a/types/src/params.rs +++ b/types/src/params.rs @@ -30,7 +30,6 @@ use std::fmt; use crate::error::CallError; -use alloc::collections::BTreeMap; use anyhow::anyhow; use beef::Cow; use serde::de::{self, Deserializer, Unexpected, Visitor}; @@ -260,40 +259,6 @@ impl<'a> ParamsSequence<'a> { } } -/// [Serializable JSON-RPC parameters](https://www.jsonrpc.org/specification#parameter_structures) -/// -/// If your type implements `Into`, call that in favor of `serde_json::to:value` to -/// construct the parameters. Because `serde_json::to_value` serializes the type which allocates -/// whereas `Into` doesn't in most cases. -#[derive(Serialize, Debug, Clone)] -#[serde(untagged)] -pub enum ParamsSer<'a> { - /// Positional params (heap allocated). - Array(Vec), - /// Positional params (slice). - ArrayRef(&'a [JsonValue]), - /// Params by name. - Map(BTreeMap<&'a str, JsonValue>), -} - -impl<'a> From> for ParamsSer<'a> { - fn from(map: BTreeMap<&'a str, JsonValue>) -> Self { - Self::Map(map) - } -} - -impl<'a> From> for ParamsSer<'a> { - fn from(arr: Vec) -> Self { - Self::Array(arr) - } -} - -impl<'a> From<&'a [JsonValue]> for ParamsSer<'a> { - fn from(slice: &'a [JsonValue]) -> Self { - Self::ArrayRef(slice) - } -} - /// Id of a subscription, communicated by the server. #[derive(Debug, PartialEq, Clone, Hash, Eq, Deserialize, Serialize)] #[serde(deny_unknown_fields)] @@ -410,7 +375,7 @@ impl<'a> Id<'a> { #[cfg(test)] mod test { - use super::{Cow, Id, JsonValue, Params, ParamsSer, SubscriptionId, TwoPointZero}; + use super::{Cow, Id, JsonValue, Params, SubscriptionId, TwoPointZero}; use crate::response::SubscriptionPayload; #[test] @@ -449,23 +414,6 @@ mod test { assert_eq!(serialized, r#"[null,0,2,3,"\"3","test"]"#); } - #[test] - fn params_serialize() { - let test_vector = &[ - ("[]", ParamsSer::Array(serde_json::from_str("[]").unwrap())), - ("[42,23]", ParamsSer::Array(serde_json::from_str("[42,23]").unwrap())), - ( - r#"{"a":42,"b":null,"c":"aa"}"#, - ParamsSer::Map(serde_json::from_str(r#"{"a":42,"b":null,"c":"aa"}"#).unwrap()), - ), - ]; - - for (initial_ser, params) in test_vector { - let serialized = serde_json::to_string(params).unwrap(); - assert_eq!(&serialized, initial_ser); - } - } - #[test] fn params_parse() { let none = Params::new(None); From bbdcc384215242db9a8ab1f8190f12c2f3b2637a Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 2 Sep 2022 17:51:55 +0300 Subject: [PATCH 34/41] core: Optimise `rpc_params` to avoid allocation on error Signed-off-by: Alexandru Vasile --- core/src/client/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index 07929e8bac..1fd705fafd 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -198,7 +198,9 @@ macro_rules! rpc_params { { let mut __params = $crate::client::__reexports::ArrayParamsBuilder::new(); $( - __params.insert($param).expect(format!("Parameter `{}` cannot be serialized", stringify!($param)).as_str()); + if let Err(err) = __params.insert($param) { + panic!("Parameter `{}` cannot be serialized: {:?}", stringify!($param), err); + } )* __params.build() } From e09181afa7ceb2362dbaaeaccfd693d57e020a11 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 2 Sep 2022 18:45:41 +0300 Subject: [PATCH 35/41] params: zero allocation for empty params Signed-off-by: Alexandru Vasile --- benches/bench.rs | 39 ++++++---- core/src/client/async_client/helpers.rs | 6 +- core/src/client/mod.rs | 14 ++-- core/src/params.rs | 98 ++++++++++++------------- core/src/traits.rs | 2 +- proc-macros/src/render_client.rs | 22 ++++-- proc-macros/tests/ui/correct/basic.rs | 4 +- tests/tests/integration_tests.rs | 34 ++++----- tests/tests/proc_macros.rs | 8 +- tests/tests/resource_limiting.rs | 38 +++++----- 10 files changed, 137 insertions(+), 128 deletions(-) diff --git a/benches/bench.rs b/benches/bench.rs index 4bb8a91665..99fc81e58a 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -6,7 +6,7 @@ use futures_util::future::{join_all, FutureExt}; use futures_util::stream::FuturesUnordered; use helpers::{http_client, ws_client, SUB_METHOD_NAME, UNSUB_METHOD_NAME}; use jsonrpsee::core::client::{ClientT, SubscriptionClientT}; -use jsonrpsee::core::params::{ArrayParamsBuilder, BatchRequestBuilder, EmptyParams, ObjectParamsBuilder}; +use jsonrpsee::core::params::{ArrayParams, BatchRequestBuilder, ObjectParams}; use jsonrpsee::core::traits::ToRpcParams; use jsonrpsee::http_client::HeaderMap; use jsonrpsee::types::{Id, RequestSer}; @@ -74,13 +74,13 @@ pub fn jsonrpsee_types_v2(crit: &mut Criterion) { v2_serialize(request); }) }); - // Construct the serialized request using the `ArrayParamsBuilder`. + // Construct the serialized request using the `ArrayParams`. crit.bench_function("jsonrpsee_types_array_params", |b| { b.iter(|| { - let mut builder = ArrayParamsBuilder::new(); + let mut builder = ArrayParams::new(); builder.insert(1u64).unwrap(); builder.insert(2u32).unwrap(); - let params = builder.build().to_rpc_params().expect("Valid params"); + let params = builder.to_rpc_params().expect("Valid params"); let request = RequestSer::new(&Id::Number(0), "say_hello", params); v2_serialize(request); }) @@ -95,12 +95,12 @@ pub fn jsonrpsee_types_v2(crit: &mut Criterion) { v2_serialize(request); }) }); - // Construct the serialized request using the `ObjectParamsBuilder`. + // Construct the serialized request using the `ObjectParams`. crit.bench_function("jsonrpsee_types_object_params", |b| { b.iter(|| { - let mut builder = ObjectParamsBuilder::new(); + let mut builder = ObjectParams::new(); builder.insert("key", 1u32).unwrap(); - let params = builder.build().to_rpc_params().expect("Valid params"); + let params = builder.to_rpc_params().expect("Valid params"); let request = RequestSer::new(&Id::Number(0), "say_hello", params); v2_serialize(request); }) @@ -155,7 +155,7 @@ fn round_trip(rt: &TokioRuntime, crit: &mut Criterion, client: Arc let bench_name = format!("{}/{}", name, method); crit.bench_function(&request.group_name(&bench_name), |b| { b.to_async(rt).iter(|| async { - black_box(client.request::(method, EmptyParams).await.unwrap()); + black_box(client.request::(method, ArrayParams::new()).await.unwrap()); }) }); } @@ -166,7 +166,10 @@ fn sub_round_trip(rt: &TokioRuntime, crit: &mut Criterion, client: Arc(SUB_METHOD_NAME, EmptyParams, UNSUB_METHOD_NAME).await.unwrap(), + client + .subscribe::(SUB_METHOD_NAME, ArrayParams::new(), UNSUB_METHOD_NAME) + .await + .unwrap(), ); }) }); @@ -178,7 +181,7 @@ fn sub_round_trip(rt: &TokioRuntime, crit: &mut Criterion, client: Arc(SUB_METHOD_NAME, EmptyParams, UNSUB_METHOD_NAME) + .subscribe::(SUB_METHOD_NAME, ArrayParams::new(), UNSUB_METHOD_NAME) .await .unwrap() }) @@ -198,7 +201,7 @@ fn sub_round_trip(rt: &TokioRuntime, crit: &mut Criterion, client: Arc(SUB_METHOD_NAME, EmptyParams, UNSUB_METHOD_NAME) + .subscribe::(SUB_METHOD_NAME, ArrayParams::new(), UNSUB_METHOD_NAME) .await .unwrap() }) @@ -227,7 +230,7 @@ fn batch_round_trip( for batch_size in [2, 5, 10, 50, 100usize].iter() { let mut batch = BatchRequestBuilder::new(); for _ in 0..*batch_size { - batch.insert(method, EmptyParams).unwrap(); + batch.insert(method, ArrayParams::new()).unwrap(); } group.throughput(Throughput::Elements(*batch_size as u64)); group.bench_with_input(BenchmarkId::from_parameter(batch_size), batch_size, |b, _| { @@ -264,7 +267,7 @@ fn ws_concurrent_conn_calls(rt: &TokioRuntime, crit: &mut Criterion, url: &str, let futs = FuturesUnordered::new(); for _ in 0..10 { - futs.push(client.request::(methods[0], EmptyParams)); + futs.push(client.request::(methods[0], ArrayParams::new())); } join_all(futs).await; @@ -305,7 +308,11 @@ fn ws_concurrent_conn_subs(rt: &TokioRuntime, crit: &mut Criterion, url: &str, n for _ in 0..10 { let fut = client - .subscribe::(SUB_METHOD_NAME, EmptyParams, UNSUB_METHOD_NAME) + .subscribe::( + SUB_METHOD_NAME, + ArrayParams::new(), + UNSUB_METHOD_NAME, + ) .then(|sub| async move { let mut s = sub.unwrap(); @@ -338,7 +345,7 @@ fn http_concurrent_conn_calls(rt: &TokioRuntime, crit: &mut Criterion, url: &str |clients| async { let tasks = clients.map(|client| { rt.spawn(async move { - client.request::(method, EmptyParams).await.unwrap(); + client.request::(method, ArrayParams::new()).await.unwrap(); }) }); join_all(tasks).await; @@ -370,7 +377,7 @@ fn http_custom_headers_round_trip( crit.bench_function(&request.group_name(&bench_name), |b| { b.to_async(rt).iter(|| async { - black_box(client.request::(method_name, EmptyParams).await.unwrap()); + black_box(client.request::(method_name, ArrayParams::new()).await.unwrap()); }) }); } diff --git a/core/src/client/async_client/helpers.rs b/core/src/client/async_client/helpers.rs index 151b144e2f..b19c2206c1 100644 --- a/core/src/client/async_client/helpers.rs +++ b/core/src/client/async_client/helpers.rs @@ -36,7 +36,7 @@ use jsonrpsee_types::error::CallError; use jsonrpsee_types::response::SubscriptionError; use jsonrpsee_types::{ErrorResponse, Id, Notification, RequestSer, Response, SubscriptionId, SubscriptionResponse}; use serde_json::Value as JsonValue; -use crate::params::ArrayParamsBuilder; +use crate::params::ArrayParams; use crate::traits::ToRpcParams; /// Attempts to process a batch response. @@ -223,9 +223,9 @@ pub(crate) fn build_unsubscribe_message( ) -> Option { let (unsub_req_id, _, unsub, sub_id) = manager.remove_subscription(sub_req_id, sub_id)?; - let mut params = ArrayParamsBuilder::new(); + let mut params = ArrayParams::new(); params.insert(sub_id).ok()?; - let params = params.build().to_rpc_params().ok()?; + let params = params.to_rpc_params().ok()?; let raw = serde_json::to_string(&RequestSer::new(&unsub_req_id, &unsub, params)).ok()?; Some(RequestMessage { raw, id: unsub_req_id, send_back: None }) diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index 1fd705fafd..075e81d2e3 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -50,9 +50,7 @@ pub mod __reexports { // Needs to be in scope for `ArrayParams` to implement it. pub use crate::traits::ToRpcParams; // Main builder object for constructing the rpc parameters. - pub use crate::params::ArrayParamsBuilder; - // Empty rpc parameters for empty macro. - pub use crate::params::EmptyParams; + pub use crate::params::ArrayParams; } cfg_async_client! { @@ -183,7 +181,7 @@ pub trait TransportReceiverT: 'static { async fn receive(&mut self) -> Result; } -/// Convert the given values to a [`crate::params::ArrayParamsBuilder`], or empty tuple as expected by a +/// Convert the given values to a [`crate::params::ArrayParams`] as expected by a /// jsonrpsee Client (http or websocket). /// /// # Panics @@ -192,17 +190,17 @@ pub trait TransportReceiverT: 'static { #[macro_export] macro_rules! rpc_params { () => { - $crate::client::__reexports::EmptyParams + $crate::client::__reexports::ArrayParams::new() }; ($($param:expr),*) => { { - let mut __params = $crate::client::__reexports::ArrayParamsBuilder::new(); + let mut params = $crate::client::__reexports::ArrayParams::new(); $( - if let Err(err) = __params.insert($param) { + if let Err(err) = params.insert($param) { panic!("Parameter `{}` cannot be serialized: {:?}", stringify!($param), err); } )* - __params.build() + params } }; } diff --git a/core/src/params.rs b/core/src/params.rs index 6b39be84a7..e4af5cd979 100644 --- a/core/src/params.rs +++ b/core/src/params.rs @@ -49,6 +49,7 @@ mod params_builder { #[derive(Debug)] pub(crate) struct ParamsBuilder { bytes: Vec, + start: char, end: char, } @@ -56,9 +57,7 @@ mod params_builder { /// Construct a new [`ParamsBuilder`] with custom start and end tokens. /// The inserted values are wrapped by the _start_ and _end_ tokens. fn new(start: char, end: char) -> Self { - let mut bytes = Vec::with_capacity(PARAM_BYTES_CAPACITY); - bytes.push(start as u8); - ParamsBuilder { bytes, end } + ParamsBuilder { bytes: Vec::new(), start, end } } /// Construct a new [`ParamsBuilder`] for positional parameters equivalent to a JSON array object. @@ -71,9 +70,25 @@ mod params_builder { Self::new('{', '}') } + /// Initialize the internal vector if it is empty: + /// - allocate [`PARAM_BYTES_CAPACITY`] to avoid resizing + /// - add the `start` character. + /// + /// # Note + /// + /// Initialization is needed prior to inserting elements. + fn maybe_initialize(&mut self) { + if self.bytes.is_empty() { + self.bytes.reserve(PARAM_BYTES_CAPACITY); + self.bytes.push(self.start as u8); + } + } + /// Insert a named value (key, value) pair into the builder. /// The _name_ and _value_ are delimited by the `:` token. pub(crate) fn insert_named(&mut self, name: &str, value: P) -> Result<(), serde_json::Error> { + self.maybe_initialize(); + serde_json::to_writer(&mut self.bytes, name)?; self.bytes.push(b':'); serde_json::to_writer(&mut self.bytes, &value)?; @@ -84,6 +99,8 @@ mod params_builder { /// Insert a plain value into the builder. pub(crate) fn insert(&mut self, value: P) -> Result<(), serde_json::Error> { + self.maybe_initialize(); + serde_json::to_writer(&mut self.bytes, &value)?; self.bytes.push(b','); @@ -91,7 +108,11 @@ mod params_builder { } /// Finish the building process and return a JSON compatible string. - pub(crate) fn build(mut self) -> String { + pub(crate) fn build(mut self) -> Option { + if self.bytes.is_empty() { + return None; + } + let idx = self.bytes.len() - 1; if self.bytes[idx] == b',' { self.bytes[idx] = self.end as u8; @@ -100,7 +121,7 @@ mod params_builder { } // Safety: This is safe because JSON does not emit invalid UTF-8. - unsafe { String::from_utf8_unchecked(self.bytes) } + Some(unsafe { String::from_utf8_unchecked(self.bytes) }) } } } @@ -112,20 +133,19 @@ mod params_builder { /// /// ```rust /// -/// use jsonrpsee_core::params::ObjectParamsBuilder; +/// use jsonrpsee_core::params::ObjectParams; /// -/// let mut builder = ObjectParamsBuilder::new(); +/// let mut builder = ObjectParams::new(); /// builder.insert("param1", 1); /// builder.insert("param2", "abc"); -/// let params = builder.build(); /// /// // Use RPC parameters... /// ``` #[derive(Debug)] -pub struct ObjectParamsBuilder(params_builder::ParamsBuilder); +pub struct ObjectParams(params_builder::ParamsBuilder); -impl ObjectParamsBuilder { - /// Construct a new [`ObjectParamsBuilder`]. +impl ObjectParams { + /// Construct a new [`ObjectParams`]. pub fn new() -> Self { Self::default() } @@ -135,26 +155,21 @@ impl ObjectParamsBuilder { pub fn insert(&mut self, name: &str, value: P) -> Result<(), serde_json::Error> { self.0.insert_named(name, value) } - - /// Finish the building process and return a JSON compatible string. - pub fn build(self) -> ObjectParams { - ObjectParams(self.0.build()) - } } -impl Default for ObjectParamsBuilder { +impl Default for ObjectParams { fn default() -> Self { Self(params_builder::ParamsBuilder::named()) } } -/// Object RPC parameters stored as a JSON Map object `{ key: value }`. -#[derive(Clone, Debug)] -pub struct ObjectParams(String); - impl ToRpcParams for ObjectParams { fn to_rpc_params(self) -> Result>, Error> { - RawValue::from_string(self.0).map(Some).map_err(Error::ParseError) + if let Some(json) = self.0.build() { + RawValue::from_string(json).map(Some).map_err(Error::ParseError) + } else { + Ok(None) + } } } @@ -165,20 +180,19 @@ impl ToRpcParams for ObjectParams { /// /// ```rust /// -/// use jsonrpsee_core::params::ArrayParamsBuilder; +/// use jsonrpsee_core::params::ArrayParams; /// -/// let mut builder = ArrayParamsBuilder::new(); +/// let mut builder = ArrayParams::new(); /// builder.insert("param1"); /// builder.insert(1); -/// let params = builder.build(); /// /// // Use RPC parameters... /// ``` #[derive(Debug)] -pub struct ArrayParamsBuilder(params_builder::ParamsBuilder); +pub struct ArrayParams(params_builder::ParamsBuilder); -impl ArrayParamsBuilder { - /// Construct a new [`ArrayParamsBuilder`]. +impl ArrayParams { + /// Construct a new [`ArrayParams`]. pub fn new() -> Self { Self::default() } @@ -187,26 +201,21 @@ impl ArrayParamsBuilder { pub fn insert(&mut self, value: P) -> Result<(), serde_json::Error> { self.0.insert(value) } - - /// Finish the building process and return a JSON compatible string. - pub fn build(self) -> ArrayParams { - ArrayParams(self.0.build()) - } } -impl Default for ArrayParamsBuilder { +impl Default for ArrayParams { fn default() -> Self { Self(params_builder::ParamsBuilder::positional()) } } -/// Array RPC parameters stored as a JSON Array object `[ value0, value1, .., valueN ]`. -#[derive(Clone, Debug)] -pub struct ArrayParams(String); - impl ToRpcParams for ArrayParams { fn to_rpc_params(self) -> Result>, Error> { - RawValue::from_string(self.0).map(Some).map_err(Error::ParseError) + if let Some(json) = self.0.build() { + RawValue::from_string(json).map(Some).map_err(Error::ParseError) + } else { + Ok(None) + } } } @@ -235,14 +244,3 @@ impl<'a> BatchRequestBuilder<'a> { self.0 } } - -/// Empty RPC parameters that perform no allocation. -#[derive(Clone, Debug)] -pub struct EmptyParams; - -/// Custom implementation for empty RPC parameters. -impl ToRpcParams for EmptyParams { - fn to_rpc_params(self) -> Result>, Error> { - Ok(None) - } -} diff --git a/core/src/traits.rs b/core/src/traits.rs index 6265ab8de9..2fd9235451 100644 --- a/core/src/traits.rs +++ b/core/src/traits.rs @@ -35,7 +35,7 @@ use serde_json::value::RawValue; /// /// # Note /// -/// Please consider using the [`crate::params::ArrayParamsBuilder`] and [`crate::params::ObjectParamsBuilder`] than +/// Please consider using the [`crate::params::ArrayParams`] and [`crate::params::ObjectParams`] than /// implementing this trait. /// /// # Examples diff --git a/proc-macros/src/render_client.rs b/proc-macros/src/render_client.rs index 6c565ae4a6..652f3486ff 100644 --- a/proc-macros/src/render_client.rs +++ b/proc-macros/src/render_client.rs @@ -154,7 +154,7 @@ impl RpcDescription { if params.is_empty() { return quote!({ - #jsonrpsee::core::params::EmptyParams + #jsonrpsee::core::params::ArrayParams::new() }); } @@ -170,18 +170,26 @@ impl RpcDescription { quote!(#name, #value) }); quote!({ - let mut builder = #jsonrpsee::core::params::ObjectParamsBuilder::new(); - #( builder.insert( #params_insert ).expect(format!("Parameters {} must be valid", stringify!(#params_insert)).as_str()); )* - builder.build() + let mut params = #jsonrpsee::core::params::ObjectParams::new(); + #( + if let Err(err) = params.insert( #params_insert ) { + panic!("Parameter `{}` cannot be serialized: {:?}", stringify!( #params_insert ), err); + } + )* + params }) } ParamKind::Array => { // Throw away the type. let params = params.iter().map(|(param, _param_type)| param); quote!({ - let mut builder = #jsonrpsee::core::params::ArrayParamsBuilder::new(); - #( builder.insert( #params ).expect(format!("Parameters {} must be valid", stringify!(#params)).as_str()); )* - builder.build() + let mut params = #jsonrpsee::core::params::ArrayParams::new(); + #( + if let Err(err) = params.insert( #params ) { + panic!("Parameter `{}` cannot be serialized: {:?}", stringify!( #params ), err); + } + )* + params }) } } diff --git a/proc-macros/tests/ui/correct/basic.rs b/proc-macros/tests/ui/correct/basic.rs index 06ecfbe420..4aabddfaf7 100644 --- a/proc-macros/tests/ui/correct/basic.rs +++ b/proc-macros/tests/ui/correct/basic.rs @@ -5,7 +5,7 @@ use std::net::SocketAddr; use jsonrpsee::core::{async_trait, client::ClientT, RpcResult}; use jsonrpsee::proc_macros::rpc; use jsonrpsee::rpc_params; -use jsonrpsee::core::params::{ArrayParams, EmptyParams}; +use jsonrpsee::core::params::ArrayParams; use jsonrpsee::types::SubscriptionResult; use jsonrpsee::ws_client::*; use jsonrpsee::ws_server::{SubscriptionSink, WsServerBuilder}; @@ -106,7 +106,7 @@ async fn main() { assert_eq!(client.array_params(vec![1]).await.unwrap(), 1); assert_eq!(client.request::("foo_array_params", rpc_params![Vec::::new()]).await.unwrap(), 0); - assert_eq!(client.request::("foo_optional_param", rpc_params![]).await.unwrap(), false); + assert_eq!(client.request::("foo_optional_param", rpc_params![]).await.unwrap(), false); assert_eq!(client.request::("foo_optional_param", rpc_params![1]).await.unwrap(), true); let mut sub = client.sub().await.unwrap(); diff --git a/tests/tests/integration_tests.rs b/tests/tests/integration_tests.rs index f32b306e0a..b06976b196 100644 --- a/tests/tests/integration_tests.rs +++ b/tests/tests/integration_tests.rs @@ -35,7 +35,7 @@ use helpers::{http_server, http_server_with_access_control, websocket_server, we use hyper::http::HeaderValue; use jsonrpsee::core::client::{ClientT, IdKind, Subscription, SubscriptionClientT}; use jsonrpsee::core::error::SubscriptionClosed; -use jsonrpsee::core::params::{ArrayParams, BatchRequestBuilder, EmptyParams}; +use jsonrpsee::core::params::{ArrayParams, BatchRequestBuilder}; use jsonrpsee::core::{Error, JsonValue}; use jsonrpsee::http_client::HttpClientBuilder; use jsonrpsee::http_server::AccessControlBuilder; @@ -176,8 +176,8 @@ async fn http_concurrent_method_call_limits_works() { let client = HttpClientBuilder::default().max_concurrent_requests(1).build(&uri).unwrap(); let (first, second) = tokio::join!( - client.request::("say_hello", rpc_params!()), - client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params!()), + client.request::("say_hello", rpc_params![]), ); assert!(first.is_ok()); @@ -295,7 +295,7 @@ async fn ws_making_more_requests_than_allowed_should_not_deadlock() { for _ in 0..6 { let c = client.clone(); - requests.push(tokio::spawn(async move { c.request::("say_hello", rpc_params![]).await })); + requests.push(tokio::spawn(async move { c.request::("say_hello", rpc_params![]).await })); } for req in requests { @@ -316,7 +316,7 @@ async fn http_making_more_requests_than_allowed_should_not_deadlock() { for _ in 0..6 { let c = client.clone(); - requests.push(tokio::spawn(async move { c.request::("say_hello", rpc_params![]).await })); + requests.push(tokio::spawn(async move { c.request::("say_hello", rpc_params![]).await })); } for req in requests { @@ -487,7 +487,7 @@ async fn ws_server_notify_client_on_disconnect() { tokio::spawn(async move { let client = WsClientBuilder::default().build(&server_url).await.unwrap(); // Validate server is up. - client.request::("say_hello", rpc_params![]).await.unwrap(); + client.request::("say_hello", rpc_params![]).await.unwrap(); // Signal client is waiting for the server to disconnect. up_tx.send(()).unwrap(); @@ -534,7 +534,7 @@ async fn ws_server_notify_client_on_disconnect_with_closed_server() { let client = WsClientBuilder::default().build(&server_url).await.unwrap(); // Validate server is up. - client.request::("say_hello", rpc_params![]).await.unwrap(); + client.request::("say_hello", rpc_params![]).await.unwrap(); // Stop the server. server_handle.stop().unwrap().await; @@ -556,7 +556,7 @@ async fn ws_server_cancels_subscriptions_on_reset_conn() { for _ in 0..10 { subs.push( client - .subscribe::("subscribe_sleep", rpc_params![], "unsubscribe_sleep") + .subscribe::("subscribe_sleep", rpc_params![], "unsubscribe_sleep") .await .unwrap(), ); @@ -632,7 +632,7 @@ async fn ws_server_pipe_from_stream_should_cancel_tasks_immediately() { for _ in 0..10 { subs.push( - client.subscribe::("subscribe_sleep", rpc_params![], "unsubscribe_sleep").await.unwrap(), + client.subscribe::("subscribe_sleep", rpc_params![], "unsubscribe_sleep").await.unwrap(), ) } @@ -651,7 +651,7 @@ async fn ws_server_pipe_from_stream_can_be_reused() { let (addr, _handle) = websocket_server_with_subscription().await; let client = WsClientBuilder::default().build(&format!("ws://{}", addr)).await.unwrap(); let sub = client - .subscribe::("can_reuse_subscription", rpc_params![], "u_can_reuse_subscription") + .subscribe::("can_reuse_subscription", rpc_params![], "u_can_reuse_subscription") .await .unwrap(); @@ -715,19 +715,19 @@ async fn ws_server_limit_subs_per_conn_works() { for _ in 0..10 { subs1.push( - c1.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever") + c1.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever") .await .unwrap(), ); subs2.push( - c2.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever") + c2.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever") .await .unwrap(), ); } - let err1 = c1.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever").await; - let err2 = c1.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever").await; + let err1 = c1.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever").await; + let err2 = c1.subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever").await; let data = "\"Exceeded max limit of 10\""; @@ -777,7 +777,7 @@ async fn ws_server_unsub_methods_should_ignore_sub_limit() { for _ in 0..10 { subs.push( client - .subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever") + .subscribe::("subscribe_forever", rpc_params![], "unsubscribe_forever") .await .unwrap(), ); @@ -1000,7 +1000,7 @@ async fn ws_host_filtering_wildcard_works() { let server_url = format!("ws://{}", addr); let client = WsClientBuilder::default().build(&server_url).await.unwrap(); - assert!(client.request::("say_hello", rpc_params![]).await.is_ok()); + assert!(client.request::("say_hello", rpc_params![]).await.is_ok()); } #[tokio::test] @@ -1024,5 +1024,5 @@ async fn http_host_filtering_wildcard_works() { let server_url = format!("http://{}", addr); let client = HttpClientBuilder::default().build(&server_url).unwrap(); - assert!(client.request::("say_hello", rpc_params![]).await.is_ok()); + assert!(client.request::("say_hello", rpc_params![]).await.is_ok()); } diff --git a/tests/tests/proc_macros.rs b/tests/tests/proc_macros.rs index b5ede7b8f9..e82dedd30b 100644 --- a/tests/tests/proc_macros.rs +++ b/tests/tests/proc_macros.rs @@ -196,7 +196,7 @@ mod rpc_impl { } // Use generated implementations of server and client. -use jsonrpsee::core::params::{ArrayParams, ObjectParams, ObjectParamsBuilder}; +use jsonrpsee::core::params::{ArrayParams, ObjectParams}; use rpc_impl::{RpcClient, RpcServer, RpcServerImpl}; pub async fn websocket_server() -> SocketAddr { @@ -356,9 +356,8 @@ async fn calls_with_bad_params() { ); // Sub with faulty params as map. - let mut params = ObjectParamsBuilder::new(); + let mut params = ObjectParams::new(); params.insert("val", "0x0").unwrap(); - let params = params.build(); let err: Error = client.subscribe::("foo_echo", params, "foo_unsubscribe_echo").await.unwrap_err(); @@ -367,10 +366,9 @@ async fn calls_with_bad_params() { ); // Call with faulty params as map. - let mut params = ObjectParamsBuilder::new(); + let mut params = ObjectParams::new(); params.insert("param_a", 1).unwrap(); params.insert("param_b", 2).unwrap(); - let params = params.build(); let err: Error = client.request::("foo_foo", params).await.unwrap_err(); assert!( diff --git a/tests/tests/resource_limiting.rs b/tests/tests/resource_limiting.rs index cd0ac9841e..03deb39d0e 100644 --- a/tests/tests/resource_limiting.rs +++ b/tests/tests/resource_limiting.rs @@ -29,7 +29,7 @@ use std::time::Duration; use futures::StreamExt; use jsonrpsee::core::client::{ClientT, SubscriptionClientT}; -use jsonrpsee::core::params::EmptyParams; +use jsonrpsee::core::params::ArrayParams; use jsonrpsee::core::Error; use jsonrpsee::http_client::HttpClientBuilder; use jsonrpsee::http_server::{HttpServerBuilder, HttpServerHandle}; @@ -187,10 +187,10 @@ async fn run_tests_on_ws_server(server_addr: SocketAddr, server_handle: WsServer // 2 CPU units (default) per call, so 4th call exceeds cap let (pass1, pass2, pass3, fail) = tokio::join!( - client.request::("say_hello", rpc_params!()), - client.request::("say_hello", rpc_params![]), - client.request::("say_hello", rpc_params![]), - client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params!()), + client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params![]), ); assert!(pass1.is_ok()); @@ -200,11 +200,11 @@ async fn run_tests_on_ws_server(server_addr: SocketAddr, server_handle: WsServer // 3 CPU units per call, so 3rd call exceeds CPU cap, but we can still get on MEM let (pass_cpu1, pass_cpu2, fail_cpu, pass_mem, fail_mem) = tokio::join!( - client.request::("expensive_call", rpc_params![]), - client.request::("expensive_call", rpc_params![]), - client.request::("expensive_call", rpc_params![]), - client.request::("memory_hog", rpc_params![]), - client.request::("memory_hog", rpc_params![]), + client.request::("expensive_call", rpc_params![]), + client.request::("expensive_call", rpc_params![]), + client.request::("expensive_call", rpc_params![]), + client.request::("memory_hog", rpc_params![]), + client.request::("memory_hog", rpc_params![]), ); assert!(pass_cpu1.is_ok()); @@ -218,8 +218,8 @@ async fn run_tests_on_ws_server(server_addr: SocketAddr, server_handle: WsServer // // Thus, we can't assume that all subscriptions drop their resources instantly anymore. let (pass1, pass2) = tokio::join!( - client.subscribe::("subscribe_hello", rpc_params![], "unsubscribe_hello"), - client.subscribe::("subscribe_hello", rpc_params![], "unsubscribe_hello"), + client.subscribe::("subscribe_hello", rpc_params![], "unsubscribe_hello"), + client.subscribe::("subscribe_hello", rpc_params![], "unsubscribe_hello"), ); assert!(pass1.is_ok()); @@ -227,9 +227,9 @@ async fn run_tests_on_ws_server(server_addr: SocketAddr, server_handle: WsServer // 3 CPU units (manually set for subscriptions) per call, so 3th call exceeds cap let (pass1, pass2, fail) = tokio::join!( - client.subscribe::("subscribe_hello_limit", rpc_params![], "unsubscribe_hello_limit"), - client.subscribe::("subscribe_hello_limit", rpc_params![], "unsubscribe_hello_limit"), - client.subscribe::("subscribe_hello_limit", rpc_params![], "unsubscribe_hello_limit"), + client.subscribe::("subscribe_hello_limit", rpc_params![], "unsubscribe_hello_limit"), + client.subscribe::("subscribe_hello_limit", rpc_params![], "unsubscribe_hello_limit"), + client.subscribe::("subscribe_hello_limit", rpc_params![], "unsubscribe_hello_limit"), ); assert!(pass1.is_ok()); @@ -245,10 +245,10 @@ async fn run_tests_on_http_server(server_addr: SocketAddr, server_handle: HttpSe // 2 CPU units (default) per call, so 4th call exceeds cap let (a, b, c, d) = tokio::join!( - client.request::("say_hello", rpc_params![]), - client.request::("say_hello", rpc_params![]), - client.request::("say_hello", rpc_params![]), - client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params![]), + client.request::("say_hello", rpc_params![]), ); // HTTP does not guarantee ordering From 5c85471ab746b350db03c0dae141ef5cc57c5539 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 2 Sep 2022 18:53:44 +0300 Subject: [PATCH 36/41] examples: Add copyright back Signed-off-by: Alexandru Vasile --- examples/examples/proc_macro.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/examples/examples/proc_macro.rs b/examples/examples/proc_macro.rs index 05bedba2b5..4eef578990 100644 --- a/examples/examples/proc_macro.rs +++ b/examples/examples/proc_macro.rs @@ -1,3 +1,29 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + //! Example of using proc macro to generate working client and server. use jsonrpsee::{core::RpcResult, proc_macros::rpc}; From 202d5bc7e3710b0db7fa14527055b3be78a40ca7 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Fri, 2 Sep 2022 18:54:57 +0300 Subject: [PATCH 37/41] traits: Remove empty doc line Signed-off-by: Alexandru Vasile --- core/src/traits.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/traits.rs b/core/src/traits.rs index 2fd9235451..d3ab14453e 100644 --- a/core/src/traits.rs +++ b/core/src/traits.rs @@ -43,7 +43,6 @@ use serde_json::value::RawValue; /// - Implementation for hard-coded strings /// /// ```rust -/// /// use jsonrpsee_core::traits::ToRpcParams; /// use serde_json::value::RawValue; /// use jsonrpsee_core::Error; From 5a4a463b1da1f1ada32768ea8fbcf96e25091007 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Fri, 2 Sep 2022 18:56:45 +0300 Subject: [PATCH 38/41] Update core/src/traits.rs Co-authored-by: James Wilson --- core/src/traits.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/traits.rs b/core/src/traits.rs index d3ab14453e..e299e6a32d 100644 --- a/core/src/traits.rs +++ b/core/src/traits.rs @@ -40,7 +40,7 @@ use serde_json::value::RawValue; /// /// # Examples /// -/// - Implementation for hard-coded strings +/// ## Implementation for hard-coded strings /// /// ```rust /// use jsonrpsee_core::traits::ToRpcParams; From c1f1dcbd217abbe2820314b05b1e6f1ca34bf282 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Fri, 2 Sep 2022 19:00:36 +0300 Subject: [PATCH 39/41] Update core/src/traits.rs Co-authored-by: James Wilson --- core/src/traits.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/traits.rs b/core/src/traits.rs index e299e6a32d..9b3532236f 100644 --- a/core/src/traits.rs +++ b/core/src/traits.rs @@ -57,7 +57,7 @@ use serde_json::value::RawValue; /// } /// ``` /// -/// - Implementation for JSON serializable structures +/// ## Implementation for JSON serializable structures /// /// ```rust /// use jsonrpsee_core::traits::ToRpcParams; From 2fabe37610a43c9b8108cef66139f76434cd750e Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Mon, 5 Sep 2022 14:42:34 +0300 Subject: [PATCH 40/41] examples: Restore `proc_macro` example to origin/master Signed-off-by: Alexandru Vasile --- examples/examples/proc_macro.rs | 80 ++++++++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 11 deletions(-) diff --git a/examples/examples/proc_macro.rs b/examples/examples/proc_macro.rs index 4eef578990..f980196899 100644 --- a/examples/examples/proc_macro.rs +++ b/examples/examples/proc_macro.rs @@ -24,20 +24,78 @@ // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -//! Example of using proc macro to generate working client and server. +use std::net::SocketAddr; -use jsonrpsee::{core::RpcResult, proc_macros::rpc}; +use jsonrpsee::core::{async_trait, client::Subscription, Error}; +use jsonrpsee::proc_macros::rpc; +use jsonrpsee::types::SubscriptionResult; +use jsonrpsee::ws_client::WsClientBuilder; +use jsonrpsee::ws_server::{SubscriptionSink, WsServerBuilder, WsServerHandle}; -#[rpc(client)] -pub trait Rpc { - #[method(name = "foo")] - async fn async_method(&self, param_a: u8, param_b: String) -> RpcResult; +type ExampleHash = [u8; 32]; +type ExampleStorageKey = Vec; - #[method(name = "bar")] - fn sync_method(&self) -> RpcResult; +#[rpc(server, client, namespace = "state")] +pub trait Rpc +where + Hash: std::fmt::Debug, +{ + /// Async method call example. + #[method(name = "getKeys")] + async fn storage_keys(&self, storage_key: StorageKey, hash: Option) -> Result, Error>; - #[subscription(name = "subscribe", item = String)] - fn sub(&self); + /// Subscription that takes a `StorageKey` as input and produces a `Vec`. + #[subscription(name = "subscribeStorage" => "override", item = Vec)] + fn subscribe_storage(&self, keys: Option>); } -fn main() {} +pub struct RpcServerImpl; + +#[async_trait] +impl RpcServer for RpcServerImpl { + async fn storage_keys( + &self, + storage_key: ExampleStorageKey, + _hash: Option, + ) -> Result, Error> { + Ok(vec![storage_key]) + } + + // Note that the server's subscription method must return `SubscriptionResult`. + fn subscribe_storage( + &self, + mut sink: SubscriptionSink, + _keys: Option>, + ) -> SubscriptionResult { + let _ = sink.send(&vec![[0; 32]]); + Ok(()) + } +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + tracing_subscriber::FmtSubscriber::builder() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .try_init() + .expect("setting default subscriber failed"); + + let (server_addr, _handle) = run_server().await?; + let url = format!("ws://{}", server_addr); + + let client = WsClientBuilder::default().build(&url).await?; + assert_eq!(client.storage_keys(vec![1, 2, 3, 4], None::).await.unwrap(), vec![vec![1, 2, 3, 4]]); + + let mut sub: Subscription> = + RpcClient::::subscribe_storage(&client, None).await.unwrap(); + assert_eq!(Some(vec![[0; 32]]), sub.next().await.transpose().unwrap()); + + Ok(()) +} + +async fn run_server() -> anyhow::Result<(SocketAddr, WsServerHandle)> { + let server = WsServerBuilder::default().build("127.0.0.1:0").await?; + + let addr = server.local_addr()?; + let handle = server.start(RpcServerImpl.into_rpc())?; + Ok((addr, handle)) +} From 75d6b8b7f6fa18d84c07f0d041183a9eceed165b Mon Sep 17 00:00:00 2001 From: Alexandru Vasile Date: Mon, 5 Sep 2022 14:43:44 +0300 Subject: [PATCH 41/41] core: Remove empty case for `rpc_params` macro Signed-off-by: Alexandru Vasile --- core/src/client/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index 075e81d2e3..438d93c4db 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -189,9 +189,6 @@ pub trait TransportReceiverT: 'static { /// Panics if the serialization of parameters fails. #[macro_export] macro_rules! rpc_params { - () => { - $crate::client::__reexports::ArrayParams::new() - }; ($($param:expr),*) => { { let mut params = $crate::client::__reexports::ArrayParams::new();