1use serde::{Deserialize, Serialize};
5
6use crate::{id::Id, version::Version};
7
8#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub struct Request<T> {
15 pub jsonrpc: Version,
17
18 #[serde(skip_serializing_if = "Option::is_none")]
30 pub id: Option<Id>,
31
32 #[serde(flatten)]
33 pub body: T,
44}
45
46impl<T> Request<T> {
47 pub const fn new(body: T) -> Self {
55 Self {
56 jsonrpc: Version,
57 id: None,
58 body,
59 }
60 }
61
62 pub const fn new_with_id(id: Id, body: T) -> Self {
70 Self {
71 jsonrpc: Version,
72 id: Some(id),
73 body,
74 }
75 }
76
77 pub const fn is_notification(&self) -> bool {
88 self.id.is_none()
89 }
90}
91
92impl<T> std::fmt::Display for Request<T>
94where
95 T: std::fmt::Display + Serialize,
96{
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 match serde_json::to_string_pretty(self) {
99 Ok(json) => write!(f, "{json}"),
100 Err(_) => Err(std::fmt::Error),
101 }
102 }
103}
104
105#[cfg(test)]
107mod test {
108 use super::*;
109 use crate::{
110 id::Id,
111 tests::{assert_ser, Body},
112 };
113
114 use pretty_assertions::assert_eq;
115 use serde_json::{json, Value};
116
117 #[test]
119 fn serde() {
120 let id = Id::Num(123);
121 let body = Body {
122 method: "a_method".into(),
123 params: [0, 1, 2],
124 };
125
126 let req = Request::new_with_id(id, body);
127
128 assert!(!req.is_notification());
129
130 let ser: String = serde_json::to_string(&req).unwrap();
131 let de: Request<Body<[u8; 3]>> = serde_json::from_str(&ser).unwrap();
132
133 assert_eq!(req, de);
134 }
135
136 #[test]
138 #[should_panic(
139 expected = "called `Result::unwrap()` on an `Err` value: Error(\"missing field `jsonrpc`\", line: 1, column: 63)"
140 )]
141 fn lowercase() {
142 let id = Id::Num(123);
143 let body = Body {
144 method: "a_method".into(),
145 params: [0, 1, 2],
146 };
147
148 let req = Request::new_with_id(id, body);
149
150 let ser: String = serde_json::to_string(&req).unwrap();
151 assert_eq!(
152 ser,
153 r#"{"jsonrpc":"2.0","id":123,"method":"a_method","params":[0,1,2]}"#,
154 );
155
156 let mixed_case = r#"{"jSoNRPC":"2.0","ID":123,"method":"a_method","params":[0,1,2]}"#;
157 let de: Request<Body<[u8; 3]>> = serde_json::from_str(mixed_case).unwrap();
158 assert_eq!(de, req);
159 }
160
161 #[test]
163 fn request_null_id() {
164 let req = Request::new_with_id(
165 Id::Null,
166 Body {
167 method: "m".into(),
168 params: "p".to_string(),
169 },
170 );
171 let json = json!({
172 "jsonrpc": "2.0",
173 "id": null,
174 "method": "m",
175 "params": "p",
176 });
177
178 assert_ser(&req, &json);
179 }
180
181 #[test]
183 fn request_none_id() {
184 let req = Request::new(Body {
185 method: "a".into(),
186 params: "b".to_string(),
187 });
188 let json = json!({
189 "jsonrpc": "2.0",
190 "method": "a",
191 "params": "b",
192 });
193
194 assert_ser(&req, &json);
195 }
196
197 #[test]
199 fn request_no_params() {
200 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
201 struct NoParamMethod {
202 method: String,
203 }
204
205 let req = Request::new_with_id(
206 Id::Num(123),
207 NoParamMethod {
208 method: "asdf".to_string(),
209 },
210 );
211 let json = json!({
212 "jsonrpc": "2.0",
213 "id": 123,
214 "method": "asdf",
215 });
216
217 assert_ser(&req, &json);
218 }
219
220 #[test]
222 fn request_tagged_enums() {
223 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
224 struct GetHeight {
225 height: u64,
226 }
227
228 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
229 #[serde(tag = "method", content = "params")]
230 #[serde(rename_all = "snake_case")]
231 enum Methods {
232 GetHeight(GetHeight),
233 }
234
235 let req = Request::new_with_id(Id::Num(123), Methods::GetHeight(GetHeight { height: 0 }));
236 let json = json!({
237 "jsonrpc": "2.0",
238 "id": 123,
239 "method": "get_height",
240 "params": {
241 "height": 0,
242 },
243 });
244
245 assert_ser(&req, &json);
246 }
247
248 #[test]
250 fn request_is_expected_value() {
251 let array: [(Request<Body<[u8; 3]>>, Value); 3] = [
253 (
254 Request::new_with_id(
255 Id::Num(123),
256 Body {
257 method: "method_1".into(),
258 params: [0, 1, 2],
259 },
260 ),
261 json!({
262 "jsonrpc": "2.0",
263 "id": 123,
264 "method": "method_1",
265 "params": [0, 1, 2],
266 }),
267 ),
268 (
269 Request::new_with_id(
270 Id::Null,
271 Body {
272 method: "method_2".into(),
273 params: [3, 4, 5],
274 },
275 ),
276 json!({
277 "jsonrpc": "2.0",
278 "id": null,
279 "method": "method_2",
280 "params": [3, 4, 5],
281 }),
282 ),
283 (
284 Request::new_with_id(
285 Id::Str("string_id".into()),
286 Body {
287 method: "method_3".into(),
288 params: [6, 7, 8],
289 },
290 ),
291 json!({
292 "jsonrpc": "2.0",
293 "method": "method_3",
294 "id": "string_id",
295 "params": [6, 7, 8],
296 }),
297 ),
298 ];
299
300 for (request, expected_value) in array {
301 assert_ser(&request, &expected_value);
302 }
303 }
304
305 #[test]
307 fn deserialize_out_of_order_keys() {
308 let expected = Request::new_with_id(
309 Id::Str("id".into()),
310 Body {
311 method: "method".into(),
312 params: [0, 1, 2],
313 },
314 );
315
316 let json = json!({
317 "method": "method",
318 "id": "id",
319 "params": [0, 1, 2],
320 "jsonrpc": "2.0",
321 });
322
323 let resp = serde_json::from_value::<Request<Body<[u8; 3]>>>(json).unwrap();
324 assert_eq!(resp, expected);
325 }
326
327 #[test]
330 fn unknown_fields_and_unicode() {
331 let expected = Request::new_with_id(
332 Id::Str("id".into()),
333 Body {
334 method: "method".into(),
335 params: [0, 1, 2],
336 },
337 );
338
339 let json = json!({
340 "unknown_field": 123,
341 "method": "method",
342 "unknown_field": 123,
343 "id": "id",
344 "\nhello": 123,
345 "params": [0, 1, 2],
346 "\u{00f8}": 123,
347 "jsonrpc": "2.0",
348 "unknown_field": 123,
349 });
350
351 let resp = serde_json::from_value::<Request<Body<[u8; 3]>>>(json).unwrap();
352 assert_eq!(resp, expected);
353 }
354}