turul_mcp_json_rpc_server/
notification.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use std::collections::HashMap;
4
5use crate::{request::RequestParams, types::JsonRpcVersion};
6
7/// A JSON-RPC notification (request without an id)
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct JsonRpcNotification {
10    #[serde(rename = "jsonrpc")]
11    pub version: JsonRpcVersion,
12    pub method: String,
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub params: Option<RequestParams>,
15}
16
17impl JsonRpcNotification {
18    pub fn new(method: String, params: Option<RequestParams>) -> Self {
19        Self {
20            version: JsonRpcVersion::V2_0,
21            method,
22            params,
23        }
24    }
25
26    /// Create a new notification with no parameters
27    pub fn new_no_params(method: String) -> Self {
28        Self::new(method, None)
29    }
30
31    /// Create a new notification with object parameters
32    pub fn new_with_object_params(method: String, params: HashMap<String, Value>) -> Self {
33        Self::new(method, Some(RequestParams::Object(params)))
34    }
35
36    /// Create a new notification with array parameters
37    pub fn new_with_array_params(method: String, params: Vec<Value>) -> Self {
38        Self::new(method, Some(RequestParams::Array(params)))
39    }
40
41    /// Get a parameter by name (if params are an object)
42    pub fn get_param(&self, name: &str) -> Option<&Value> {
43        self.params.as_ref()?.get(name)
44    }
45
46    /// Get a parameter by index (if params are an array)
47    pub fn get_param_index(&self, index: usize) -> Option<&Value> {
48        self.params.as_ref()?.get_index(index)
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55    use serde_json::{from_str, json, to_string};
56
57    #[test]
58    fn test_notification_serialization() {
59        let notification = JsonRpcNotification::new_no_params("test_notification".to_string());
60
61        let json_str = to_string(&notification).unwrap();
62        let parsed: JsonRpcNotification = from_str(&json_str).unwrap();
63
64        assert_eq!(parsed.method, "test_notification");
65        assert!(parsed.params.is_none());
66    }
67
68    #[test]
69    fn test_notification_with_params() {
70        let mut params = HashMap::new();
71        params.insert("message".to_string(), json!("Hello"));
72        params.insert("level".to_string(), json!("info"));
73
74        let notification = JsonRpcNotification::new_with_object_params("log".to_string(), params);
75
76        assert_eq!(notification.get_param("message"), Some(&json!("Hello")));
77        assert_eq!(notification.get_param("level"), Some(&json!("info")));
78    }
79
80    #[test]
81    fn test_notification_json_format() {
82        let notification = JsonRpcNotification::new_no_params("ping".to_string());
83        let json_str = to_string(&notification).unwrap();
84
85        // Should not contain an "id" field
86        assert!(!json_str.contains("\"id\""));
87        assert!(json_str.contains("\"jsonrpc\":\"2.0\""));
88        assert!(json_str.contains("\"method\":\"ping\""));
89    }
90}