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(
33        method: String,
34        params: HashMap<String, Value>,
35    ) -> Self {
36        Self::new(method, Some(RequestParams::Object(params)))
37    }
38
39    /// Create a new notification with array parameters
40    pub fn new_with_array_params(method: String, params: Vec<Value>) -> Self {
41        Self::new(method, Some(RequestParams::Array(params)))
42    }
43
44    /// Get a parameter by name (if params are an object)
45    pub fn get_param(&self, name: &str) -> Option<&Value> {
46        self.params.as_ref()?.get(name)
47    }
48
49    /// Get a parameter by index (if params are an array)
50    pub fn get_param_index(&self, index: usize) -> Option<&Value> {
51        self.params.as_ref()?.get_index(index)
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use serde_json::{json, from_str, to_string};
59
60    #[test]
61    fn test_notification_serialization() {
62        let notification = JsonRpcNotification::new_no_params("test_notification".to_string());
63
64        let json_str = to_string(&notification).unwrap();
65        let parsed: JsonRpcNotification = from_str(&json_str).unwrap();
66
67        assert_eq!(parsed.method, "test_notification");
68        assert!(parsed.params.is_none());
69    }
70
71    #[test]
72    fn test_notification_with_params() {
73        let mut params = HashMap::new();
74        params.insert("message".to_string(), json!("Hello"));
75        params.insert("level".to_string(), json!("info"));
76
77        let notification = JsonRpcNotification::new_with_object_params(
78            "log".to_string(),
79            params,
80        );
81
82        assert_eq!(notification.get_param("message"), Some(&json!("Hello")));
83        assert_eq!(notification.get_param("level"), Some(&json!("info")));
84    }
85
86    #[test]
87    fn test_notification_json_format() {
88        let notification = JsonRpcNotification::new_no_params("ping".to_string());
89        let json_str = to_string(&notification).unwrap();
90        
91        // Should not contain an "id" field
92        assert!(!json_str.contains("\"id\""));
93        assert!(json_str.contains("\"jsonrpc\":\"2.0\""));
94        assert!(json_str.contains("\"method\":\"ping\""));
95    }
96}