1use crate::RpcRequest;
4use crate::rpc_message::rpc_request_parsing_error::RpcRequestParsingError;
5use crate::rpc_message::support::{extract_value, parse_method, parse_params, validate_version};
6use crate::support::get_json_type;
7use serde::ser::SerializeStruct;
8use serde::{Deserialize, Serialize, Serializer};
9use serde_json::Value;
10
11#[derive(Deserialize, Debug, Clone, PartialEq)]
17pub struct RpcNotification {
18 pub method: String,
19 pub params: Option<Value>,
20}
21
22impl RpcNotification {
23 pub fn from_value(value: Value) -> Result<RpcNotification, RpcRequestParsingError> {
35 let value_type = get_json_type(&value);
36
37 let Value::Object(mut obj) = value else {
38 return Err(RpcRequestParsingError::RequestInvalidType {
39 actual_type: value_type.to_string(),
40 });
41 };
42
43 let version_val = extract_value(&mut obj, "jsonrpc");
45 if let Err(version_result) = validate_version(version_val) {
46 let method_val = extract_value(&mut obj, "method");
48 let method = method_val.and_then(|v| v.as_str().map(|s| s.to_string()));
49 return match version_result {
50 Some(v) => Err(RpcRequestParsingError::VersionInvalid {
51 id: None, method,
53 version: v,
54 }),
55 None => Err(RpcRequestParsingError::VersionMissing {
56 id: None, method,
58 }),
59 };
60 }
61
62 let method_val = extract_value(&mut obj, "method");
64 let method = match parse_method(method_val) {
65 Ok(m) => m,
66 Err(method_result) => {
67 return match method_result {
68 Some(m) => Err(RpcRequestParsingError::MethodInvalidType {
69 id: None, method: m,
71 }),
72 None => Err(RpcRequestParsingError::MethodMissing { id: None }), };
74 }
75 };
76
77 let params_val = extract_value(&mut obj, "params");
79 let params = parse_params(params_val)?; if let Some(id_val) = extract_value(&mut obj, "id") {
83 return Err(RpcRequestParsingError::NotificationHasId {
84 method: Some(method),
85 id: id_val,
86 });
87 }
88
89 Ok(RpcNotification { method, params })
90 }
91}
92
93impl Serialize for RpcNotification {
96 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
97 where
98 S: Serializer,
99 {
100 let mut field_count = 2;
102 if self.params.is_some() {
103 field_count += 1;
104 }
105
106 let mut state = serializer.serialize_struct("RpcNotification", field_count)?;
107
108 state.serialize_field("jsonrpc", "2.0")?;
110
111 state.serialize_field("method", &self.method)?;
112
113 if let Some(params) = &self.params {
115 state.serialize_field("params", params)?;
116 }
117
118 state.end()
119 }
120}
121
122impl From<RpcRequest> for RpcNotification {
127 fn from(request: RpcRequest) -> Self {
128 RpcNotification {
129 method: request.method,
130 params: request.params,
131 }
132 }
133}
134
135impl TryFrom<Value> for RpcNotification {
141 type Error = RpcRequestParsingError;
142 fn try_from(value: Value) -> Result<RpcNotification, RpcRequestParsingError> {
143 RpcNotification::from_value(value)
144 }
145}
146
147#[cfg(test)]
152mod tests {
153 use super::*;
154 use crate::rpc_message::RpcRequestParsingError;
155 use serde_json::{json, to_value};
156
157 type Result<T> = core::result::Result<T, Box<dyn std::error::Error>>; fn notif_value_ok_params_some() -> Value {
161 json!({
162 "jsonrpc": "2.0",
163 "method": "updateState",
164 "params": {"value": 123}
165 })
166 }
167
168 fn notif_value_ok_params_none() -> Value {
169 json!({
170 "jsonrpc": "2.0",
171 "method": "ping"
172 })
173 }
174
175 fn notif_value_ok_params_arr() -> Value {
176 json!({
177 "jsonrpc": "2.0",
178 "method": "notifyUsers",
179 "params": ["user1", "user2"]
180 })
181 }
182
183 fn notif_value_fail_id_present() -> Value {
184 json!({
185 "jsonrpc": "2.0",
186 "id": 888, "method": "updateState",
188 "params": {"value": 123}
189 })
190 }
191
192 fn notif_value_fail_version_missing() -> Value {
193 json!({
194 "method": "updateState"
196 })
197 }
198
199 fn notif_value_fail_version_invalid() -> Value {
200 json!({
201 "jsonrpc": "1.0", "method": "updateState"
203 })
204 }
205
206 fn notif_value_fail_method_missing() -> Value {
207 json!({
208 "jsonrpc": "2.0"
209 })
211 }
212
213 fn notif_value_fail_method_invalid() -> Value {
214 json!({
215 "jsonrpc": "2.0",
216 "method": 123 })
218 }
219
220 fn notif_value_fail_params_invalid() -> Value {
221 json!({
222 "jsonrpc": "2.0",
223 "method": "update",
224 "params": "not-array-or-object" })
226 }
227
228 #[test]
230 fn test_rpc_notification_serialize_ok_params_some() -> Result<()> {
231 let notif = RpcNotification {
233 method: "updateState".to_string(),
234 params: Some(json!({"value": 123})),
235 };
236
237 let value = to_value(notif)?;
239
240 assert_eq!(value, notif_value_ok_params_some());
242 Ok(())
243 }
244
245 #[test]
246 fn test_rpc_notification_serialize_ok_params_none() -> Result<()> {
247 let notif = RpcNotification {
249 method: "ping".to_string(),
250 params: None,
251 };
252
253 let value = to_value(notif)?;
255
256 assert_eq!(value, notif_value_ok_params_none());
258 Ok(())
259 }
260
261 #[test]
262 fn test_rpc_notification_serialize_ok_params_arr() -> Result<()> {
263 let notif = RpcNotification {
265 method: "notifyUsers".to_string(),
266 params: Some(json!(["user1", "user2"])),
267 };
268
269 let value = to_value(notif)?;
271
272 assert_eq!(value, notif_value_ok_params_arr());
274 Ok(())
275 }
276 #[test]
280 fn test_rpc_notification_from_value_ok_params_some() -> Result<()> {
281 let value = notif_value_ok_params_some();
283 let expected = RpcNotification {
284 method: "updateState".to_string(),
285 params: Some(json!({"value": 123})),
286 };
287
288 let notification = RpcNotification::from_value(value)?;
290
291 assert_eq!(notification, expected);
293 Ok(())
294 }
295
296 #[test]
297 fn test_rpc_notification_from_value_ok_params_none() -> Result<()> {
298 let value = notif_value_ok_params_none();
300 let expected = RpcNotification {
301 method: "ping".to_string(),
302 params: None,
303 };
304
305 let notification = RpcNotification::from_value(value)?;
307
308 assert_eq!(notification, expected);
310 Ok(())
311 }
312
313 #[test]
314 fn test_rpc_notification_from_value_ok_params_arr() -> Result<()> {
315 let value = notif_value_ok_params_arr();
317 let expected = RpcNotification {
318 method: "notifyUsers".to_string(),
319 params: Some(json!(["user1", "user2"])),
320 };
321
322 let notification = RpcNotification::from_value(value)?;
324
325 assert_eq!(notification, expected);
327 Ok(())
328 }
329
330 #[test]
331 fn test_rpc_notification_from_value_fail_id_present() -> Result<()> {
332 let value = notif_value_fail_id_present();
334
335 let result = RpcNotification::from_value(value);
337
338 assert!(matches!(
340 result,
341 Err(RpcRequestParsingError::NotificationHasId { method: Some(_), id: _ })
342 ));
343 if let Err(RpcRequestParsingError::NotificationHasId { method, id }) = result {
344 assert_eq!(method.unwrap(), "updateState");
345 assert_eq!(id, json!(888));
346 } else {
347 panic!("Expected NotificationHasId error");
348 }
349 Ok(())
350 }
351
352 #[test]
353 fn test_rpc_notification_from_value_fail_version_missing() -> Result<()> {
354 let value = notif_value_fail_version_missing();
356
357 let result = RpcNotification::from_value(value);
359
360 assert!(matches!(
362 result,
363 Err(RpcRequestParsingError::VersionMissing {
364 id: None,
365 method: Some(_)
366 })
367 ));
368 if let Err(RpcRequestParsingError::VersionMissing { id, method }) = result {
369 assert!(id.is_none());
370 assert_eq!(method.unwrap(), "updateState");
371 } else {
372 panic!("Expected VersionMissing error");
373 }
374 Ok(())
375 }
376
377 #[test]
378 fn test_rpc_notification_from_value_fail_version_invalid() -> Result<()> {
379 let value = notif_value_fail_version_invalid();
381
382 let result = RpcNotification::from_value(value);
384
385 assert!(matches!(
387 result,
388 Err(RpcRequestParsingError::VersionInvalid {
389 id: None,
390 method: Some(_),
391 version: _
392 })
393 ));
394 if let Err(RpcRequestParsingError::VersionInvalid { id, method, version }) = result {
395 assert!(id.is_none());
396 assert_eq!(method.unwrap(), "updateState");
397 assert_eq!(version, json!("1.0"));
398 } else {
399 panic!("Expected VersionInvalid error");
400 }
401 Ok(())
402 }
403
404 #[test]
405 fn test_rpc_notification_from_value_fail_method_missing() -> Result<()> {
406 let value = notif_value_fail_method_missing();
408
409 let result = RpcNotification::from_value(value);
411
412 assert!(matches!(
414 result,
415 Err(RpcRequestParsingError::MethodMissing { id: None })
416 ));
417 Ok(())
418 }
419
420 #[test]
421 fn test_rpc_notification_from_value_fail_method_invalid() -> Result<()> {
422 let value = notif_value_fail_method_invalid();
424
425 let result = RpcNotification::from_value(value);
427
428 assert!(matches!(
430 result,
431 Err(RpcRequestParsingError::MethodInvalidType { id: None, method: _ })
432 ));
433 if let Err(RpcRequestParsingError::MethodInvalidType { id, method }) = result {
434 assert!(id.is_none());
435 assert_eq!(method, json!(123));
436 } else {
437 panic!("Expected MethodInvalidType error");
438 }
439 Ok(())
440 }
441
442 #[test]
443 fn test_rpc_notification_from_value_fail_params_invalid() -> Result<()> {
444 let value = notif_value_fail_params_invalid();
446
447 let result = RpcNotification::from_value(value);
449
450 assert!(matches!(
452 result,
453 Err(RpcRequestParsingError::ParamsInvalidType { actual_type: _ })
454 ));
455 if let Err(RpcRequestParsingError::ParamsInvalidType { actual_type }) = result {
456 assert_eq!(actual_type, "String");
457 } else {
458 panic!("Expected ParamsInvalidType error");
459 }
460 Ok(())
461 }
462
463 #[test]
464 fn test_rpc_notification_from_value_fail_not_object() -> Result<()> {
465 let value = json!("not an object");
467
468 let result = RpcNotification::from_value(value);
470
471 assert!(matches!(
473 result,
474 Err(RpcRequestParsingError::RequestInvalidType { actual_type: _ })
475 ));
476 if let Err(RpcRequestParsingError::RequestInvalidType { actual_type }) = result {
477 assert_eq!(actual_type, "String");
478 } else {
479 panic!("Expected RequestInvalidType error");
480 }
481 Ok(())
482 }
483 }
485