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