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}