thin_jsonrpc_client/
params.rs

1use serde_json::value::RawValue;
2use serde::Serialize;
3
4/// A trait which is implemented for anything which can be turned into
5/// valid RPC parameters.
6pub trait IntoRpcParams {
7    /// Return the params. These are expected to be encoded to JSON,
8    /// and take the form of either an array or object of parameters.
9    /// Can return `None` to avoid allocation when returning no parameters.
10    fn into_rpc_params(self) -> RpcParams;
11}
12
13/// The RPC params we'll return from our implementations of [`IntoRpcParams`].
14pub type RpcParams = Option<Box<RawValue>>;
15
16impl IntoRpcParams for RpcParams {
17    fn into_rpc_params(self) -> RpcParams {
18        self
19    }
20}
21
22/// Parameter builder to build valid "object or "named" parameters.
23/// This is the equivalent of a JSON Map object `{ key: value }`.
24///
25/// # Examples
26///
27/// ```rust
28///
29/// use thin_jsonrpc_client::params::ObjectParams;
30///
31/// let params = ObjectParams::new()
32///     .insert("param1", 1)
33///     .insert("param2", "abc");
34///
35/// // Use RPC parameters...
36/// ```
37#[derive(Clone, Debug)]
38pub struct ObjectParams {
39    bytes: Vec<u8>
40}
41
42impl ObjectParams {
43    /// Construct a new [`ObjectParams`] instance.
44    pub fn new() -> Self {
45        ObjectParams { bytes: Vec::new() }
46    }
47
48    /// Insert a new named parameter.
49    pub fn insert<P: Serialize>(mut self, name: &str, value: P) -> Self {
50        if self.bytes.is_empty() {
51            self.bytes.push(b'{');
52        } else {
53            self.bytes.push(b',');
54        }
55
56        serde_json::to_writer(&mut self.bytes, name)
57            .expect("should always be valid");
58        self.bytes.push(b':');
59        serde_json::to_writer(&mut self.bytes, &value)
60            .expect("invalid JSON");
61
62        self
63    }
64}
65
66impl IntoRpcParams for ObjectParams {
67    fn into_rpc_params(mut self) -> RpcParams {
68        if self.bytes.is_empty() {
69            return None;
70        }
71
72        self.bytes.push(b'}');
73        // Safety: This is safe because JSON does not emit invalid UTF-8:
74        let utf8_string = unsafe { String::from_utf8_unchecked(self.bytes) };
75        Some(RawValue::from_string(utf8_string).expect("valid JSON expected"))
76    }
77}
78
79/// Parameter builder to build valid "array" or "unnamed" JSON-RPC parameters.
80/// This is the equivalent of a JSON array like `[ value0, value1, .., valueN ]`.
81///
82/// # Examples
83///
84/// ```rust
85///
86/// use thin_jsonrpc_client::params::ArrayParams;
87///
88/// let params = ArrayParams::new()
89///     .insert("param1")
90///     .insert(1);
91///
92/// // Use RPC parameters...
93/// ```
94#[derive(Clone, Debug)]
95pub struct ArrayParams {
96    bytes: Vec<u8>
97}
98
99impl ArrayParams {
100    /// Construct a new [`ArrayParams`] instance.
101    pub fn new() -> Self {
102        ArrayParams { bytes: Vec::new() }
103    }
104
105    /// Insert a new parameter.
106    pub fn insert<P: Serialize>(mut self, value: P) -> Self {
107        if self.bytes.is_empty() {
108            self.bytes.push(b'[');
109        } else {
110            self.bytes.push(b',');
111        }
112
113        serde_json::to_writer(&mut self.bytes, &value)
114            .expect("invalid JSON");
115
116        self
117    }
118}
119
120impl IntoRpcParams for ArrayParams {
121    fn into_rpc_params(mut self) -> RpcParams {
122        if self.bytes.is_empty() {
123            return None;
124        }
125
126        self.bytes.push(b']');
127        // Safety: This is safe because JSON does not emit invalid UTF-8:
128        let utf8_string = unsafe { String::from_utf8_unchecked(self.bytes) };
129        Some(RawValue::from_string(utf8_string).expect("valid JSON expected"))
130    }
131}
132
133/// Construct positional/array parameters for a JSON-RPC Call.
134#[macro_export]
135macro_rules! params {
136    ($($param:expr),*) => {{
137        let mut a = $crate::params::ArrayParams::new();
138        $(
139            a = a.insert($param);
140        )*
141        a
142    }}
143}
144pub use params;
145
146// Arrays can be used as positional params.
147impl <const N: usize, P: Serialize> IntoRpcParams for [P; N] {
148    fn into_rpc_params(self) -> RpcParams {
149        let mut params = ArrayParams::new();
150        for p in self {
151            params = params.insert(p)
152        }
153        params.into_rpc_params()
154    }
155}
156
157// Tuples of different sizes can be used as positional params.
158macro_rules! impl_tuple_params {
159    ($($ident:ident)*) => {
160        impl <$($ident),*> IntoRpcParams for ($($ident,)*)
161        where $($ident: Serialize),*
162        {
163            #[allow(non_snake_case, unused_mut)]
164            fn into_rpc_params(self) -> RpcParams {
165                let ($($ident,)*) = self;
166                let mut params = ArrayParams::new();
167                $(
168                    params = params.insert($ident);
169                )*
170                params.into_rpc_params()
171            }
172        }
173    }
174}
175
176impl_tuple_params!();
177impl_tuple_params!(A);
178impl_tuple_params!(A B);
179impl_tuple_params!(A B C);
180impl_tuple_params!(A B C D);
181impl_tuple_params!(A B C D E);
182impl_tuple_params!(A B C D E F);
183impl_tuple_params!(A B C D E F G);
184impl_tuple_params!(A B C D E F G H);
185impl_tuple_params!(A B C D E F G H I);
186impl_tuple_params!(A B C D E F G H I J);
187impl_tuple_params!(A B C D E F G H I J K);
188impl_tuple_params!(A B C D E F G H I J K L);
189impl_tuple_params!(A B C D E F G H I J K L M);
190impl_tuple_params!(A B C D E F G H I J K L M N);
191impl_tuple_params!(A B C D E F G H I J K L M N O);
192impl_tuple_params!(A B C D E F G H I J K L M N O P);