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	use serde_json::value::RawValue;
37
38	/// Initial number of bytes for a parameter length.
39	const PARAM_BYTES_CAPACITY: usize = 128;
40
41	/// Generic parameter builder that serializes parameters to bytes.
42	/// This produces a JSON compatible String.
43	///
44	/// The implementation relies on `Vec<u8>` to hold the serialized
45	/// parameters in memory for the following reasons:
46	///   1. Other serialization methods than `serde_json::to_writer` would internally
47	///      have an extra heap allocation for temporarily holding the value in memory.
48	///   2. `io::Write` is not implemented for `String` required for serialization.
49	#[derive(Debug, Clone)]
50	pub(crate) struct ParamsBuilder {
51		bytes: Vec<u8>,
52		start: char,
53		end: char,
54	}
55
56	impl ParamsBuilder {
57		/// Construct a new [`ParamsBuilder`] with custom start and end tokens.
58		/// The inserted values are wrapped by the _start_ and _end_ tokens.
59		fn new(start: char, end: char) -> Self {
60			ParamsBuilder { bytes: Vec::new(), start, end }
61		}
62
63		/// Construct a new [`ParamsBuilder`] for positional parameters equivalent to a JSON array object.
64		pub(crate) fn positional() -> Self {
65			Self::new('[', ']')
66		}
67
68		/// Construct a new [`ParamsBuilder`] for named parameters equivalent to a JSON map object.
69		pub(crate) fn named() -> Self {
70			Self::new('{', '}')
71		}
72
73		/// Initialize the internal vector if it is empty:
74		///  - allocate [`PARAM_BYTES_CAPACITY`] to avoid resizing
75		///  - add the `start` character.
76		///
77		/// # Note
78		///
79		/// Initialization is needed prior to inserting elements.
80		fn maybe_initialize(&mut self) {
81			if self.bytes.is_empty() {
82				self.bytes.reserve(PARAM_BYTES_CAPACITY);
83				self.bytes.push(self.start as u8);
84			}
85		}
86
87		/// Insert a named value (key, value) pair into the builder.
88		/// The _name_ and _value_ are delimited by the `:` token.
89		pub(crate) fn insert_named<P: Serialize>(&mut self, name: &str, value: P) -> Result<(), serde_json::Error> {
90			self.maybe_initialize();
91
92			serde_json::to_writer(&mut self.bytes, name)?;
93			self.bytes.push(b':');
94			serde_json::to_writer(&mut self.bytes, &value)?;
95			self.bytes.push(b',');
96
97			Ok(())
98		}
99
100		/// Insert a plain value into the builder.
101		pub(crate) fn insert<P: Serialize>(&mut self, value: P) -> Result<(), serde_json::Error> {
102			self.maybe_initialize();
103
104			serde_json::to_writer(&mut self.bytes, &value)?;
105			self.bytes.push(b',');
106
107			Ok(())
108		}
109
110		/// Finish the building process and return a JSON object.
111		pub(crate) fn build(mut self) -> Option<Box<RawValue>> {
112			if self.bytes.is_empty() {
113				return None;
114			}
115
116			let idx = self.bytes.len() - 1;
117			if self.bytes[idx] == b',' {
118				self.bytes[idx] = self.end as u8;
119			} else {
120				self.bytes.push(self.end as u8);
121			}
122
123			// Safety: This is safe because JSON does not emit invalid UTF-8.
124			let json_str = unsafe { String::from_utf8_unchecked(self.bytes) };
125			Some(RawValue::from_string(json_str).expect("Valid JSON String; qed"))
126		}
127	}
128}
129
130/// Parameter builder that serializes named value parameters to a JSON compatible string.
131/// This is the equivalent of a JSON Map object `{ key: value }`.
132///
133/// # Examples
134///
135/// ```rust
136///
137/// use jsonrpsee_core::params::ObjectParams;
138///
139/// let mut builder = ObjectParams::new();
140/// builder.insert("param1", 1);
141/// builder.insert("param2", "abc");
142///
143/// // Use RPC parameters...
144/// ```
145#[derive(Debug, Clone)]
146pub struct ObjectParams(params_builder::ParamsBuilder);
147
148impl ObjectParams {
149	/// Construct a new [`ObjectParams`].
150	pub fn new() -> Self {
151		Self::default()
152	}
153
154	/// Insert a named value (key, value) pair into the builder.
155	/// The _name_ and _value_ are delimited by the `:` token.
156	pub fn insert<P: Serialize>(&mut self, name: &str, value: P) -> Result<(), serde_json::Error> {
157		self.0.insert_named(name, value)
158	}
159}
160
161impl Default for ObjectParams {
162	fn default() -> Self {
163		Self(params_builder::ParamsBuilder::named())
164	}
165}
166
167impl ToRpcParams for ObjectParams {
168	fn to_rpc_params(self) -> Result<Option<Box<RawValue>>, serde_json::Error> {
169		Ok(self.0.build())
170	}
171}
172
173/// Parameter builder that serializes plain value parameters to a JSON compatible string.
174/// This is the equivalent of a JSON Array object `[ value0, value1, .., valueN ]`.
175///
176/// # Examples
177///
178/// ```rust
179///
180/// use jsonrpsee_core::params::ArrayParams;
181///
182/// let mut builder = ArrayParams::new();
183/// builder.insert("param1");
184/// builder.insert(1);
185///
186/// // Use RPC parameters...
187/// ```
188#[derive(Debug, Clone)]
189pub struct ArrayParams(params_builder::ParamsBuilder);
190
191impl ArrayParams {
192	/// Construct a new [`ArrayParams`].
193	pub fn new() -> Self {
194		Self::default()
195	}
196
197	/// Insert a plain value into the builder.
198	pub fn insert<P: Serialize>(&mut self, value: P) -> Result<(), serde_json::Error> {
199		self.0.insert(value)
200	}
201}
202
203impl Default for ArrayParams {
204	fn default() -> Self {
205		Self(params_builder::ParamsBuilder::positional())
206	}
207}
208
209impl ToRpcParams for ArrayParams {
210	fn to_rpc_params(self) -> Result<Option<Box<RawValue>>, serde_json::Error> {
211		Ok(self.0.build())
212	}
213}
214
215/// Initial number of parameters in a batch request.
216const BATCH_PARAMS_NUM_CAPACITY: usize = 4;
217
218/// Error representing an empty batch request.
219#[derive(Debug, Clone, Copy, thiserror::Error)]
220#[error("Empty batch request is not allowed")]
221pub struct EmptyBatchRequest;
222
223/// Request builder that serializes RPC parameters to construct a valid batch parameter.
224/// This is the equivalent of chaining multiple RPC requests.
225#[derive(Clone, Debug, Default)]
226pub struct BatchRequestBuilder<'a>(Vec<(&'a str, Option<Box<RawValue>>)>);
227
228impl<'a> BatchRequestBuilder<'a> {
229	/// Construct a new [`BatchRequestBuilder`].
230	pub fn new() -> Self {
231		Self(Vec::with_capacity(BATCH_PARAMS_NUM_CAPACITY))
232	}
233
234	/// Inserts the RPC method with provided parameters into the builder.
235	pub fn insert<Params: ToRpcParams>(&mut self, method: &'a str, value: Params) -> Result<(), serde_json::Error> {
236		self.0.push((method, value.to_rpc_params()?));
237		Ok(())
238	}
239
240	/// Finish the building process and return a valid batch parameter.
241	#[allow(clippy::type_complexity)]
242	pub fn build(self) -> Result<Vec<(&'a str, Option<Box<RawValue>>)>, EmptyBatchRequest> {
243		if self.0.is_empty() { Err(EmptyBatchRequest) } else { Ok(self.0) }
244	}
245
246	/// Get an iterator over the batch request.
247	pub fn iter(&self) -> impl Iterator<Item = (&'a str, Option<&RawValue>)> {
248		self.0.iter().map(|(method, params)| (*method, params.as_deref()))
249	}
250}
251
252impl<'a> IntoIterator for BatchRequestBuilder<'a> {
253	type Item = (&'a str, Option<Box<RawValue>>);
254	type IntoIter = std::vec::IntoIter<Self::Item>;
255
256	fn into_iter(self) -> Self::IntoIter {
257		self.0.into_iter()
258	}
259}