jsonrpsee_core/
params.rs

1// Copyright 2019-2021 Parity Technologies (UK) Ltd.
2//
3// Permission is hereby granted, free of charge, to any
4// person obtaining a copy of this software and associated
5// documentation files (the "Software"), to deal in the
6// Software without restriction, including without
7// limitation the rights to use, copy, modify, merge,
8// publish, distribute, sublicense, and/or sell copies of
9// the Software, and to permit persons to whom the Software
10// is furnished to do so, subject to the following
11// conditions:
12//
13// The above copyright notice and this permission notice
14// shall be included in all copies or substantial portions
15// of the Software.
16//
17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
18// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
19// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
21// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
24// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25// DEALINGS IN THE SOFTWARE.
26
27//! RPC parameters.
28
29use crate::traits::ToRpcParams;
30use serde::Serialize;
31use serde_json::value::RawValue;
32
33/// Helper module for building parameters.
34mod params_builder {
35	use serde::Serialize;
36
37	/// Initial number of bytes for a parameter length.
38	const PARAM_BYTES_CAPACITY: usize = 128;
39
40	/// Generic parameter builder that serializes parameters to bytes.
41	/// This produces a JSON compatible String.
42	///
43	/// The implementation relies on `Vec<u8>` to hold the serialized
44	/// parameters in memory for the following reasons:
45	///   1. Other serialization methods than `serde_json::to_writer` would internally
46	///      have an extra heap allocation for temporarily holding the value in memory.
47	///   2. `io::Write` is not implemented for `String` required for serialization.
48	#[derive(Debug, Clone)]
49	pub(crate) struct ParamsBuilder {
50		bytes: Vec<u8>,
51		start: char,
52		end: char,
53	}
54
55	impl ParamsBuilder {
56		/// Construct a new [`ParamsBuilder`] with custom start and end tokens.
57		/// The inserted values are wrapped by the _start_ and _end_ tokens.
58		fn new(start: char, end: char) -> Self {
59			ParamsBuilder { bytes: Vec::new(), start, end }
60		}
61
62		/// Construct a new [`ParamsBuilder`] for positional parameters equivalent to a JSON array object.
63		pub(crate) fn positional() -> Self {
64			Self::new('[', ']')
65		}
66
67		/// Construct a new [`ParamsBuilder`] for named parameters equivalent to a JSON map object.
68		pub(crate) fn named() -> Self {
69			Self::new('{', '}')
70		}
71
72		/// Initialize the internal vector if it is empty:
73		///  - allocate [`PARAM_BYTES_CAPACITY`] to avoid resizing
74		///  - add the `start` character.
75		///
76		/// # Note
77		///
78		/// Initialization is needed prior to inserting elements.
79		fn maybe_initialize(&mut self) {
80			if self.bytes.is_empty() {
81				self.bytes.reserve(PARAM_BYTES_CAPACITY);
82				self.bytes.push(self.start as u8);
83			}
84		}
85
86		/// Insert a named value (key, value) pair into the builder.
87		/// The _name_ and _value_ are delimited by the `:` token.
88		pub(crate) fn insert_named<P: Serialize>(&mut self, name: &str, value: P) -> Result<(), serde_json::Error> {
89			self.maybe_initialize();
90
91			serde_json::to_writer(&mut self.bytes, name)?;
92			self.bytes.push(b':');
93			serde_json::to_writer(&mut self.bytes, &value)?;
94			self.bytes.push(b',');
95
96			Ok(())
97		}
98
99		/// Insert a plain value into the builder.
100		pub(crate) fn insert<P: Serialize>(&mut self, value: P) -> Result<(), serde_json::Error> {
101			self.maybe_initialize();
102
103			serde_json::to_writer(&mut self.bytes, &value)?;
104			self.bytes.push(b',');
105
106			Ok(())
107		}
108
109		/// Finish the building process and return a JSON compatible string.
110		pub(crate) fn build(mut self) -> Option<String> {
111			if self.bytes.is_empty() {
112				return None;
113			}
114
115			let idx = self.bytes.len() - 1;
116			if self.bytes[idx] == b',' {
117				self.bytes[idx] = self.end as u8;
118			} else {
119				self.bytes.push(self.end as u8);
120			}
121
122			// Safety: This is safe because JSON does not emit invalid UTF-8.
123			Some(unsafe { String::from_utf8_unchecked(self.bytes) })
124		}
125	}
126}
127
128/// Parameter builder that serializes named value parameters to a JSON compatible string.
129/// This is the equivalent of a JSON Map object `{ key: value }`.
130///
131/// # Examples
132///
133/// ```rust
134///
135/// use jsonrpsee_core::params::ObjectParams;
136///
137/// let mut builder = ObjectParams::new();
138/// builder.insert("param1", 1);
139/// builder.insert("param2", "abc");
140///
141/// // Use RPC parameters...
142/// ```
143#[derive(Debug, Clone)]
144pub struct ObjectParams(params_builder::ParamsBuilder);
145
146impl ObjectParams {
147	/// Construct a new [`ObjectParams`].
148	pub fn new() -> Self {
149		Self::default()
150	}
151
152	/// Insert a named value (key, value) pair into the builder.
153	/// The _name_ and _value_ are delimited by the `:` token.
154	pub fn insert<P: Serialize>(&mut self, name: &str, value: P) -> Result<(), serde_json::Error> {
155		self.0.insert_named(name, value)
156	}
157}
158
159impl Default for ObjectParams {
160	fn default() -> Self {
161		Self(params_builder::ParamsBuilder::named())
162	}
163}
164
165impl ToRpcParams for ObjectParams {
166	fn to_rpc_params(self) -> Result<Option<Box<RawValue>>, serde_json::Error> {
167		if let Some(json) = self.0.build() {
168			RawValue::from_string(json).map(Some)
169		} else {
170			Ok(None)
171		}
172	}
173}
174
175/// Parameter builder that serializes plain value parameters to a JSON compatible string.
176/// This is the equivalent of a JSON Array object `[ value0, value1, .., valueN ]`.
177///
178/// # Examples
179///
180/// ```rust
181///
182/// use jsonrpsee_core::params::ArrayParams;
183///
184/// let mut builder = ArrayParams::new();
185/// builder.insert("param1");
186/// builder.insert(1);
187///
188/// // Use RPC parameters...
189/// ```
190#[derive(Debug, Clone)]
191pub struct ArrayParams(params_builder::ParamsBuilder);
192
193impl ArrayParams {
194	/// Construct a new [`ArrayParams`].
195	pub fn new() -> Self {
196		Self::default()
197	}
198
199	/// Insert a plain value into the builder.
200	pub fn insert<P: Serialize>(&mut self, value: P) -> Result<(), serde_json::Error> {
201		self.0.insert(value)
202	}
203}
204
205impl Default for ArrayParams {
206	fn default() -> Self {
207		Self(params_builder::ParamsBuilder::positional())
208	}
209}
210
211impl ToRpcParams for ArrayParams {
212	fn to_rpc_params(self) -> Result<Option<Box<RawValue>>, serde_json::Error> {
213		if let Some(json) = self.0.build() {
214			RawValue::from_string(json).map(Some)
215		} else {
216			Ok(None)
217		}
218	}
219}
220
221/// Initial number of parameters in a batch request.
222const BATCH_PARAMS_NUM_CAPACITY: usize = 4;
223
224/// Error representing an empty batch request.
225#[derive(Debug, Clone, Copy, thiserror::Error)]
226#[error("Empty batch request is not allowed")]
227pub struct EmptyBatchRequest;
228
229/// Request builder that serializes RPC parameters to construct a valid batch parameter.
230/// This is the equivalent of chaining multiple RPC requests.
231#[derive(Clone, Debug, Default)]
232pub struct BatchRequestBuilder<'a>(Vec<(&'a str, Option<Box<RawValue>>)>);
233
234impl<'a> BatchRequestBuilder<'a> {
235	/// Construct a new [`BatchRequestBuilder`].
236	pub fn new() -> Self {
237		Self(Vec::with_capacity(BATCH_PARAMS_NUM_CAPACITY))
238	}
239
240	/// Inserts the RPC method with provided parameters into the builder.
241	pub fn insert<Params: ToRpcParams>(&mut self, method: &'a str, value: Params) -> Result<(), serde_json::Error> {
242		self.0.push((method, value.to_rpc_params()?));
243		Ok(())
244	}
245
246	/// Finish the building process and return a valid batch parameter.
247	#[allow(clippy::type_complexity)]
248	pub fn build(self) -> Result<Vec<(&'a str, Option<Box<RawValue>>)>, EmptyBatchRequest> {
249		if self.0.is_empty() {
250			Err(EmptyBatchRequest)
251		} else {
252			Ok(self.0)
253		}
254	}
255
256	/// Get an iterator over the batch request.
257	pub fn iter(&self) -> impl Iterator<Item = (&'a str, Option<&RawValue>)> {
258		self.0.iter().map(|(method, params)| (*method, params.as_deref()))
259	}
260}
261
262impl<'a> IntoIterator for BatchRequestBuilder<'a> {
263	type Item = (&'a str, Option<Box<RawValue>>);
264	type IntoIter = std::vec::IntoIter<Self::Item>;
265
266	fn into_iter(self) -> Self::IntoIter {
267		self.0.into_iter()
268	}
269}