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}