turul_mcp_json_rpc_server/
dispatch.rs

1use serde_json::Value;
2
3use crate::{
4    error::JsonRpcError, notification::JsonRpcNotification, request::JsonRpcRequest,
5    response::JsonRpcResponse, types::RequestId,
6};
7
8/// Enum representing different types of JSON-RPC messages
9#[derive(Debug, Clone)]
10pub enum JsonRpcMessage {
11    Request(JsonRpcRequest),
12    Notification(JsonRpcNotification),
13}
14
15/// Result of parsing and processing a JSON-RPC message
16#[derive(Debug, Clone)]
17pub enum JsonRpcMessageResult {
18    /// A response to a request
19    Response(JsonRpcResponse),
20    /// An error response
21    Error(JsonRpcError),
22    /// No response needed (for notifications)
23    NoResponse,
24}
25
26impl JsonRpcMessageResult {
27    /// Convert to JSON string if there's a response to send
28    pub fn to_json_string(&self) -> Option<String> {
29        match self {
30            JsonRpcMessageResult::Response(response) => serde_json::to_string(response).ok(),
31            JsonRpcMessageResult::Error(error) => serde_json::to_string(error).ok(),
32            JsonRpcMessageResult::NoResponse => None,
33        }
34    }
35
36    /// Check if this result represents an error
37    pub fn is_error(&self) -> bool {
38        matches!(self, JsonRpcMessageResult::Error(_))
39    }
40
41    /// Check if this result needs a response
42    pub fn needs_response(&self) -> bool {
43        !matches!(self, JsonRpcMessageResult::NoResponse)
44    }
45}
46
47/// Parse a JSON string into a JSON-RPC message
48pub fn parse_json_rpc_message(json_str: &str) -> Result<JsonRpcMessage, JsonRpcError> {
49    let value: Value = serde_json::from_str(json_str).map_err(|_| JsonRpcError::parse_error())?;
50
51    // Check if it's a valid JSON-RPC message
52    if !value.is_object() {
53        return Err(JsonRpcError::invalid_request(None));
54    }
55
56    let obj = value.as_object().unwrap();
57
58    // Check JSON-RPC version
59    match obj.get("jsonrpc") {
60        Some(version) if version == "2.0" => {}
61        _ => return Err(JsonRpcError::invalid_request(None)),
62    }
63
64    // Check if it has an ID (request) or not (notification)
65    if obj.contains_key("id") {
66        // It's a request
67        serde_json::from_value::<JsonRpcRequest>(value.clone())
68            .map(JsonRpcMessage::Request)
69            .map_err(|_| {
70                // Try to extract ID for error response
71                let id = obj.get("id").and_then(|v| match v {
72                    Value::String(s) => Some(RequestId::String(s.clone())),
73                    Value::Number(n) => n.as_i64().map(RequestId::Number),
74                    _ => None,
75                });
76                JsonRpcError::invalid_request(id)
77            })
78    } else {
79        // It's a notification
80        serde_json::from_value::<JsonRpcNotification>(value)
81            .map(JsonRpcMessage::Notification)
82            .map_err(|_| JsonRpcError::invalid_request(None))
83    }
84}
85
86/// Utility functions for working with JSON-RPC messages
87impl JsonRpcMessage {
88    /// Get the method name
89    pub fn method(&self) -> &str {
90        match self {
91            JsonRpcMessage::Request(req) => &req.method,
92            JsonRpcMessage::Notification(notif) => &notif.method,
93        }
94    }
95
96    /// Check if this is a request (has ID)
97    pub fn is_request(&self) -> bool {
98        matches!(self, JsonRpcMessage::Request(_))
99    }
100
101    /// Check if this is a notification (no ID)
102    pub fn is_notification(&self) -> bool {
103        matches!(self, JsonRpcMessage::Notification(_))
104    }
105
106    /// Get the request ID if this is a request
107    pub fn request_id(&self) -> Option<&RequestId> {
108        match self {
109            JsonRpcMessage::Request(req) => Some(&req.id),
110            JsonRpcMessage::Notification(_) => None,
111        }
112    }
113}
114
115/// Parse multiple JSON-RPC messages from a single JSON string
116/// This handles both single messages and potential future batch support
117pub fn parse_json_rpc_messages(json_str: &str) -> Vec<Result<JsonRpcMessage, JsonRpcError>> {
118    // For now, we only support single messages (JSON-RPC 2.0 removed batch support)
119    vec![parse_json_rpc_message(json_str)]
120}
121
122/// Create a simple success response
123pub fn create_success_response(id: RequestId, result: Value) -> JsonRpcMessageResult {
124    JsonRpcMessageResult::Response(JsonRpcResponse::success(id, result))
125}
126
127/// Create a simple error response
128pub fn create_error_response(
129    id: Option<RequestId>,
130    code: i64,
131    message: &str,
132) -> JsonRpcMessageResult {
133    let error_obj = crate::error::JsonRpcErrorObject {
134        code,
135        message: message.to_string(),
136        data: None,
137    };
138    JsonRpcMessageResult::Error(JsonRpcError::new(id, error_obj))
139}
140
141#[cfg(test)]
142mod tests {
143    use super::*;
144    use serde_json::json;
145
146    #[test]
147    fn test_parse_valid_request() {
148        let json = r#"{"jsonrpc": "2.0", "method": "test", "id": 1}"#;
149        let message = parse_json_rpc_message(json).unwrap();
150
151        assert!(message.is_request());
152        assert_eq!(message.method(), "test");
153        assert_eq!(message.request_id(), Some(&RequestId::Number(1)));
154    }
155
156    #[test]
157    fn test_parse_valid_notification() {
158        let json = r#"{"jsonrpc": "2.0", "method": "notify"}"#;
159        let message = parse_json_rpc_message(json).unwrap();
160
161        assert!(message.is_notification());
162        assert_eq!(message.method(), "notify");
163        assert_eq!(message.request_id(), None);
164    }
165
166    #[test]
167    fn test_parse_invalid_json() {
168        let json = r#"{"jsonrpc": "2.0", "method": "test""#; // Invalid JSON
169        let result = parse_json_rpc_message(json);
170
171        assert!(result.is_err());
172        let error = result.unwrap_err();
173        assert_eq!(error.error.code, -32700); // Parse error
174    }
175
176    #[test]
177    fn test_parse_invalid_version() {
178        let json = r#"{"jsonrpc": "1.0", "method": "test", "id": 1}"#;
179        let result = parse_json_rpc_message(json);
180
181        assert!(result.is_err());
182        let error = result.unwrap_err();
183        assert_eq!(error.error.code, -32600); // Invalid request
184    }
185
186    #[test]
187    fn test_message_result_to_json() {
188        let response = create_success_response(RequestId::Number(1), json!({"result": "success"}));
189
190        let json_str = response.to_json_string().unwrap();
191        assert!(json_str.contains("\"result\""));
192        assert!(json_str.contains("\"jsonrpc\":\"2.0\""));
193    }
194
195    #[test]
196    fn test_message_result_properties() {
197        let success = create_success_response(RequestId::Number(1), json!({}));
198        let error = create_error_response(Some(RequestId::Number(1)), -32601, "Not found");
199        let no_response = JsonRpcMessageResult::NoResponse;
200
201        assert!(!success.is_error());
202        assert!(success.needs_response());
203
204        assert!(error.is_error());
205        assert!(error.needs_response());
206
207        assert!(!no_response.is_error());
208        assert!(!no_response.needs_response());
209    }
210}