ricecoder_external_lsp/client/
protocol.rs

1//! JSON-RPC 2.0 protocol handling
2
3use serde::{Deserialize, Serialize};
4#[allow(unused_imports)]
5use serde_json::{json, Value};
6use std::sync::atomic::{AtomicU64, Ordering};
7use std::sync::Arc;
8
9/// JSON-RPC 2.0 request ID
10pub type RequestId = u64;
11
12/// JSON-RPC 2.0 request
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct JsonRpcRequest {
15    /// JSON-RPC version (always "2.0")
16    pub jsonrpc: String,
17    /// Request method name
18    pub method: String,
19    /// Request parameters
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub params: Option<Value>,
22    /// Request ID (required for requests expecting responses)
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub id: Option<RequestId>,
25}
26
27/// JSON-RPC 2.0 response
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct JsonRpcResponse {
30    /// JSON-RPC version (always "2.0")
31    pub jsonrpc: String,
32    /// Response result (mutually exclusive with error)
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub result: Option<Value>,
35    /// Response error (mutually exclusive with result)
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub error: Option<JsonRpcError>,
38    /// Response ID (matches request ID)
39    pub id: RequestId,
40}
41
42/// JSON-RPC 2.0 error
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct JsonRpcError {
45    /// Error code
46    pub code: i32,
47    /// Error message
48    pub message: String,
49    /// Optional error data
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub data: Option<Value>,
52}
53
54/// JSON-RPC 2.0 notification (request without ID)
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct JsonRpcNotification {
57    /// JSON-RPC version (always "2.0")
58    pub jsonrpc: String,
59    /// Notification method name
60    pub method: String,
61    /// Notification parameters
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub params: Option<Value>,
64}
65
66/// JSON-RPC 2.0 message (can be request, response, or notification)
67#[derive(Debug, Clone, Serialize, Deserialize)]
68#[serde(untagged)]
69pub enum JsonRpcMessage {
70    /// Request message
71    Request(JsonRpcRequest),
72    /// Response message
73    Response(JsonRpcResponse),
74    /// Notification message
75    Notification(JsonRpcNotification),
76}
77
78/// Handles JSON-RPC 2.0 protocol communication
79pub struct JsonRpcHandler {
80    /// Next request ID to use
81    next_id: Arc<AtomicU64>,
82}
83
84impl JsonRpcHandler {
85    /// Create a new JSON-RPC handler
86    pub fn new() -> Self {
87        Self {
88            next_id: Arc::new(AtomicU64::new(1)),
89        }
90    }
91
92    /// Generate the next request ID
93    pub fn next_request_id(&self) -> RequestId {
94        self.next_id.fetch_add(1, Ordering::SeqCst)
95    }
96
97    /// Create a JSON-RPC request
98    pub fn create_request(
99        &self,
100        method: impl Into<String>,
101        params: Option<Value>,
102    ) -> JsonRpcRequest {
103        JsonRpcRequest {
104            jsonrpc: "2.0".to_string(),
105            method: method.into(),
106            params,
107            id: Some(self.next_request_id()),
108        }
109    }
110
111    /// Create a JSON-RPC notification (no response expected)
112    pub fn create_notification(
113        &self,
114        method: impl Into<String>,
115        params: Option<Value>,
116    ) -> JsonRpcNotification {
117        JsonRpcNotification {
118            jsonrpc: "2.0".to_string(),
119            method: method.into(),
120            params,
121        }
122    }
123
124    /// Serialize a request to JSON
125    pub fn serialize_request(&self, request: &JsonRpcRequest) -> crate::error::Result<String> {
126        serde_json::to_string(request)
127            .map_err(|e| crate::error::ExternalLspError::ProtocolError(e.to_string()))
128    }
129
130    /// Serialize a notification to JSON
131    pub fn serialize_notification(&self, notification: &JsonRpcNotification) -> crate::error::Result<String> {
132        serde_json::to_string(notification)
133            .map_err(|e| crate::error::ExternalLspError::ProtocolError(e.to_string()))
134    }
135
136    /// Parse a JSON-RPC response
137    pub fn parse_response(&self, json: &str) -> crate::error::Result<JsonRpcResponse> {
138        serde_json::from_str(json)
139            .map_err(|e| crate::error::ExternalLspError::ProtocolError(format!("Failed to parse response: {}", e)))
140    }
141
142    /// Parse a JSON-RPC notification
143    pub fn parse_notification(&self, json: &str) -> crate::error::Result<JsonRpcNotification> {
144        serde_json::from_str(json)
145            .map_err(|e| crate::error::ExternalLspError::ProtocolError(format!("Failed to parse notification: {}", e)))
146    }
147
148    /// Parse a JSON-RPC message (can be response or notification)
149    pub fn parse_message(&self, json: &str) -> crate::error::Result<JsonRpcMessage> {
150        serde_json::from_str(json)
151            .map_err(|e| crate::error::ExternalLspError::ProtocolError(format!("Failed to parse message: {}", e)))
152    }
153
154    /// Check if a response indicates an error
155    pub fn is_error_response(response: &JsonRpcResponse) -> bool {
156        response.error.is_some()
157    }
158
159    /// Extract error message from response
160    pub fn extract_error_message(response: &JsonRpcResponse) -> Option<String> {
161        response.error.as_ref().map(|e| e.message.clone())
162    }
163
164    /// Create a JSON-RPC error response
165    pub fn create_error_response(
166        id: RequestId,
167        code: i32,
168        message: impl Into<String>,
169    ) -> JsonRpcResponse {
170        JsonRpcResponse {
171            jsonrpc: "2.0".to_string(),
172            result: None,
173            error: Some(JsonRpcError {
174                code,
175                message: message.into(),
176                data: None,
177            }),
178            id,
179        }
180    }
181}
182
183impl Default for JsonRpcHandler {
184    fn default() -> Self {
185        Self::new()
186    }
187}
188
189#[cfg(test)]
190mod tests {
191    use super::*;
192
193    #[test]
194    fn test_create_request() {
195        let handler = JsonRpcHandler::new();
196        let request = handler.create_request("initialize", Some(json!({"processId": 1234})));
197
198        assert_eq!(request.jsonrpc, "2.0");
199        assert_eq!(request.method, "initialize");
200        assert!(request.id.is_some());
201        assert!(request.params.is_some());
202    }
203
204    #[test]
205    fn test_create_notification() {
206        let handler = JsonRpcHandler::new();
207        let notification = handler.create_notification("initialized", None);
208
209        assert_eq!(notification.jsonrpc, "2.0");
210        assert_eq!(notification.method, "initialized");
211        assert!(notification.params.is_none());
212    }
213
214    #[test]
215    fn test_serialize_request() {
216        let handler = JsonRpcHandler::new();
217        let request = handler.create_request("test", None);
218        let json = handler.serialize_request(&request).unwrap();
219
220        assert!(json.contains("\"jsonrpc\":\"2.0\""));
221        assert!(json.contains("\"method\":\"test\""));
222    }
223
224    #[test]
225    fn test_parse_response() {
226        let handler = JsonRpcHandler::new();
227        let json = r#"{"jsonrpc":"2.0","result":{"key":"value"},"id":1}"#;
228        let response = handler.parse_response(json).unwrap();
229
230        assert_eq!(response.jsonrpc, "2.0");
231        assert_eq!(response.id, 1);
232        assert!(response.result.is_some());
233        assert!(response.error.is_none());
234    }
235
236    #[test]
237    fn test_parse_error_response() {
238        let handler = JsonRpcHandler::new();
239        let json = r#"{"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid Request"},"id":1}"#;
240        let response = handler.parse_response(json).unwrap();
241
242        assert_eq!(response.jsonrpc, "2.0");
243        assert_eq!(response.id, 1);
244        assert!(response.result.is_none());
245        assert!(response.error.is_some());
246        assert_eq!(response.error.unwrap().code, -32600);
247    }
248
249    #[test]
250    fn test_request_id_increments() {
251        let handler = JsonRpcHandler::new();
252        let id1 = handler.next_request_id();
253        let id2 = handler.next_request_id();
254        let id3 = handler.next_request_id();
255
256        assert_eq!(id1, 1);
257        assert_eq!(id2, 2);
258        assert_eq!(id3, 3);
259    }
260
261    #[test]
262    fn test_is_error_response() {
263        let error_response = JsonRpcResponse {
264            jsonrpc: "2.0".to_string(),
265            result: None,
266            error: Some(JsonRpcError {
267                code: -32600,
268                message: "Invalid Request".to_string(),
269                data: None,
270            }),
271            id: 1,
272        };
273
274        assert!(JsonRpcHandler::is_error_response(&error_response));
275
276        let success_response = JsonRpcResponse {
277            jsonrpc: "2.0".to_string(),
278            result: Some(json!({"key": "value"})),
279            error: None,
280            id: 1,
281        };
282
283        assert!(!JsonRpcHandler::is_error_response(&success_response));
284    }
285}