Skip to main content

lsp_server_tokio/
error.rs

1//! Error types for LSP JSON-RPC messages.
2//!
3//! This module provides the [`ErrorCode`] enum with all LSP specification error codes
4//! and the [`ResponseError`] struct for constructing error responses.
5
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9/// LSP and JSON-RPC 2.0 error codes.
10///
11/// These error codes are defined by the JSON-RPC 2.0 specification and the LSP specification.
12/// The codes are represented as i32 values using `#[repr(i32)]`.
13///
14/// # Error Code Ranges
15///
16/// - `-32700` to `-32600`: JSON-RPC 2.0 defined errors
17/// - `-32099` to `-32000`: JSON-RPC reserved for implementation-defined server errors
18/// - `-32899` to `-32800`: LSP reserved for LSP-specific errors
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[repr(i32)]
21pub enum ErrorCode {
22    // JSON-RPC 2.0 defined errors
23    /// Invalid JSON was received by the server.
24    ParseError = -32700,
25    /// The JSON sent is not a valid Request object.
26    InvalidRequest = -32600,
27    /// The method does not exist / is not available.
28    MethodNotFound = -32601,
29    /// Invalid method parameter(s).
30    InvalidParams = -32602,
31    /// Internal JSON-RPC error.
32    InternalError = -32603,
33
34    // JSON-RPC reserved range (used by LSP)
35    /// The server has not been initialized.
36    ServerNotInitialized = -32002,
37    /// Unknown error code (used for unmapped errors).
38    UnknownErrorCode = -32001,
39
40    // LSP specific (3.17.0+)
41    /// A request failed but it was syntactically correct.
42    RequestFailed = -32803,
43    /// The server cancelled the request.
44    ServerCancelled = -32802,
45    /// The server detected that the content of a document got modified outside normal conditions.
46    ContentModified = -32801,
47    /// The client has canceled a request and a server has detected the cancel.
48    RequestCancelled = -32800,
49}
50
51impl From<ErrorCode> for i32 {
52    fn from(code: ErrorCode) -> Self {
53        code as i32
54    }
55}
56
57impl TryFrom<i32> for ErrorCode {
58    type Error = i32;
59
60    /// Attempts to convert an i32 to an `ErrorCode`.
61    ///
62    /// Returns `Err(code)` if the code is not a known error code.
63    fn try_from(code: i32) -> Result<Self, Self::Error> {
64        match code {
65            -32700 => Ok(ErrorCode::ParseError),
66            -32600 => Ok(ErrorCode::InvalidRequest),
67            -32601 => Ok(ErrorCode::MethodNotFound),
68            -32602 => Ok(ErrorCode::InvalidParams),
69            -32603 => Ok(ErrorCode::InternalError),
70            -32002 => Ok(ErrorCode::ServerNotInitialized),
71            -32001 => Ok(ErrorCode::UnknownErrorCode),
72            -32803 => Ok(ErrorCode::RequestFailed),
73            -32802 => Ok(ErrorCode::ServerCancelled),
74            -32801 => Ok(ErrorCode::ContentModified),
75            -32800 => Ok(ErrorCode::RequestCancelled),
76            _ => Err(code),
77        }
78    }
79}
80
81/// An error response in JSON-RPC format.
82///
83/// Contains a numeric error code, a human-readable message, and optional additional data.
84/// The `code` field is stored as `i32` rather than `ErrorCode` to support unknown error codes
85/// from clients or other servers.
86///
87/// # Examples
88///
89/// ```
90/// use lsp_server_tokio::{ResponseError, ErrorCode};
91///
92/// // Create a simple error
93/// let error = ResponseError::new(ErrorCode::MethodNotFound, "Method not found");
94///
95/// // Create an error with additional data
96/// let error_with_data = ResponseError::new(ErrorCode::InvalidParams, "Missing required field")
97///     .with_data(serde_json::json!({"field": "uri"}));
98/// ```
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct ResponseError {
101    /// A number indicating the error type.
102    pub code: i32,
103    /// A string providing a short description of the error.
104    pub message: String,
105    /// A primitive or structured value with additional information.
106    /// This may be omitted.
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub data: Option<Value>,
109}
110
111impl ResponseError {
112    /// Creates a new `ResponseError` with the given error code and message.
113    ///
114    /// The `data` field is set to `None`.
115    pub fn new(code: ErrorCode, message: impl Into<String>) -> Self {
116        Self {
117            code: code as i32,
118            message: message.into(),
119            data: None,
120        }
121    }
122
123    /// Creates a new `ResponseError` from a raw error code and message.
124    ///
125    /// Use this when dealing with unknown or custom error codes.
126    pub fn new_raw(code: i32, message: impl Into<String>) -> Self {
127        Self {
128            code,
129            message: message.into(),
130            data: None,
131        }
132    }
133
134    /// Adds additional data to this error.
135    ///
136    /// Consumes self and returns a new `ResponseError` with the data attached.
137    #[must_use]
138    pub fn with_data(mut self, data: Value) -> Self {
139        self.data = Some(data);
140        self
141    }
142
143    /// Returns the error code as an `ErrorCode` enum, if it maps to a known code.
144    #[must_use]
145    pub fn error_code(&self) -> Option<ErrorCode> {
146        ErrorCode::try_from(self.code).ok()
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153
154    #[test]
155    fn error_code_to_i32() {
156        assert_eq!(i32::from(ErrorCode::ParseError), -32700);
157        assert_eq!(i32::from(ErrorCode::InvalidRequest), -32600);
158        assert_eq!(i32::from(ErrorCode::MethodNotFound), -32601);
159        assert_eq!(i32::from(ErrorCode::InvalidParams), -32602);
160        assert_eq!(i32::from(ErrorCode::InternalError), -32603);
161        assert_eq!(i32::from(ErrorCode::ServerNotInitialized), -32002);
162        assert_eq!(i32::from(ErrorCode::UnknownErrorCode), -32001);
163        assert_eq!(i32::from(ErrorCode::RequestFailed), -32803);
164        assert_eq!(i32::from(ErrorCode::ServerCancelled), -32802);
165        assert_eq!(i32::from(ErrorCode::ContentModified), -32801);
166        assert_eq!(i32::from(ErrorCode::RequestCancelled), -32800);
167    }
168
169    #[test]
170    fn i32_to_error_code() {
171        assert_eq!(ErrorCode::try_from(-32700), Ok(ErrorCode::ParseError));
172        assert_eq!(ErrorCode::try_from(-32601), Ok(ErrorCode::MethodNotFound));
173        assert_eq!(ErrorCode::try_from(-32800), Ok(ErrorCode::RequestCancelled));
174        assert_eq!(ErrorCode::try_from(-99999), Err(-99999));
175    }
176
177    #[test]
178    fn response_error_construction() {
179        let error = ResponseError::new(ErrorCode::MethodNotFound, "Method not found");
180        assert_eq!(error.code, -32601);
181        assert_eq!(error.message, "Method not found");
182        assert!(error.data.is_none());
183    }
184
185    #[test]
186    fn response_error_with_data() {
187        let error = ResponseError::new(ErrorCode::InvalidParams, "Invalid params")
188            .with_data(serde_json::json!({"field": "uri"}));
189        assert_eq!(error.code, -32602);
190        assert!(error.data.is_some());
191        assert_eq!(error.data.unwrap()["field"], "uri");
192    }
193
194    #[test]
195    fn response_error_serialization_without_data() {
196        let error = ResponseError::new(ErrorCode::ParseError, "Parse error");
197        let json = serde_json::to_string(&error).unwrap();
198        // data field should be omitted when None
199        assert!(!json.contains("data"));
200        assert!(json.contains("-32700"));
201        assert!(json.contains("Parse error"));
202    }
203
204    #[test]
205    fn response_error_serialization_with_data() {
206        let error = ResponseError::new(ErrorCode::InvalidParams, "Missing field")
207            .with_data(serde_json::json!({"missing": "uri"}));
208        let json = serde_json::to_string(&error).unwrap();
209        assert!(json.contains("data"));
210        assert!(json.contains("missing"));
211    }
212
213    #[test]
214    fn response_error_deserialization() {
215        let json = r#"{"code":-32601,"message":"Method not found"}"#;
216        let error: ResponseError = serde_json::from_str(json).unwrap();
217        assert_eq!(error.code, -32601);
218        assert_eq!(error.message, "Method not found");
219        assert!(error.data.is_none());
220    }
221
222    #[test]
223    fn response_error_deserialization_with_data() {
224        let json = r#"{"code":-32602,"message":"Invalid","data":{"field":"uri"}}"#;
225        let error: ResponseError = serde_json::from_str(json).unwrap();
226        assert_eq!(error.code, -32602);
227        assert!(error.data.is_some());
228    }
229
230    #[test]
231    fn response_error_raw_code() {
232        let error = ResponseError::new_raw(-99999, "Custom error");
233        assert_eq!(error.code, -99999);
234        assert!(error.error_code().is_none());
235    }
236
237    #[test]
238    fn skip_serializing_if_works() {
239        let error = ResponseError::new(ErrorCode::InternalError, "Error");
240        let json = serde_json::to_value(&error).unwrap();
241        let obj = json.as_object().unwrap();
242        // The "data" key should not exist when data is None
243        assert!(!obj.contains_key("data"));
244    }
245}