lsp_bridge/
error.rs

1//! Error types and utilities for LSP Bridge.
2
3use std::fmt;
4use thiserror::Error;
5
6/// Primary error type for the LSP Bridge
7#[derive(Error, Debug)]
8pub enum LspBridgeError {
9    /// LSP-related errors (existing LspError variants)
10    #[error("LSP error: {0}")]
11    Lsp(#[from] LspError),
12
13    /// Resource limit exceeded
14    #[error("Resource limit exceeded: {0}")]
15    ResourceLimitExceeded(String),
16
17    /// Rate limit exceeded
18    #[error("Rate limit exceeded: {0}")]
19    RateLimitExceeded(String),
20
21    /// Circuit breaker is open
22    #[error("Circuit breaker open: {0}")]
23    CircuitBreakerOpen(String),
24
25    /// Input validation failed
26    #[error("Input validation failed: {0}")]
27    ValidationFailed(String),
28
29    /// Security violation
30    #[error("Security violation: {0}")]
31    SecurityViolation(String),
32
33    /// Configuration error
34    #[error("Configuration error: {0}")]
35    Configuration(String),
36
37    /// IO error
38    #[error("IO error: {0}")]
39    Io(#[from] std::io::Error),
40
41    /// JSON serialization/deserialization error
42    #[error("JSON error: {0}")]
43    Json(#[from] serde_json::Error),
44
45    /// Timeout error
46    #[error("Timeout: {0}")]
47    Timeout(String),
48
49    /// Generic error
50    #[error("Error: {0}")]
51    Generic(String),
52}
53
54/// Result type alias for the LSP Bridge
55pub type Result<T> = std::result::Result<T, LspBridgeError>;
56
57/// Comprehensive error types for LSP Bridge operations.
58#[derive(Error, Debug)]
59pub enum LspError {
60    /// Server startup or initialization failed
61    #[error("Server startup failed: {message}")]
62    ServerStartup { message: String },
63
64    /// Server communication error
65    #[error("Server communication error: {message}")]
66    Communication { message: String },
67
68    /// Server crashed or became unresponsive
69    #[error("Server crashed: {server_id}")]
70    ServerCrash { server_id: String },
71
72    /// Protocol version mismatch
73    #[error("Protocol version mismatch: expected {expected}, got {actual}")]
74    ProtocolMismatch { expected: String, actual: String },
75
76    /// Invalid server configuration
77    #[error("Invalid server configuration: {message}")]
78    InvalidConfiguration { message: String },
79
80    /// Request timeout
81    #[error("Request timed out after {timeout_ms}ms")]
82    Timeout { timeout_ms: u64 },
83
84    /// Invalid document URI
85    #[error("Invalid document URI: {uri}")]
86    InvalidUri { uri: String },
87
88    /// Server not found
89    #[error("Server not found: {server_id}")]
90    ServerNotFound { server_id: String },
91
92    /// Feature not supported by server
93    #[error("Feature '{feature}' not supported by server {server_id}")]
94    FeatureNotSupported { feature: String, server_id: String },
95
96    /// JSON-RPC error
97    #[error("JSON-RPC error: {message}")]
98    JsonRpc { message: String },
99
100    /// IO error
101    #[error("IO error: {0}")]
102    Io(#[from] std::io::Error),
103
104    /// Serialization error
105    #[error("Serialization error: {0}")]
106    Serialization(#[from] serde_json::Error),
107
108    /// Custom error for extensibility
109    #[error("Custom error: {message}")]
110    Custom { message: String },
111}
112
113impl LspError {
114    /// Create a new server startup error
115    pub fn server_startup<S: Into<String>>(message: S) -> Self {
116        Self::ServerStartup {
117            message: message.into(),
118        }
119    }
120
121    /// Create a new communication error
122    pub fn communication<S: Into<String>>(message: S) -> Self {
123        Self::Communication {
124            message: message.into(),
125        }
126    }
127
128    /// Create a new server crash error
129    pub fn server_crash<S: Into<String>>(server_id: S) -> Self {
130        Self::ServerCrash {
131            server_id: server_id.into(),
132        }
133    }
134
135    /// Create a new invalid configuration error
136    pub fn invalid_configuration<S: Into<String>>(message: S) -> Self {
137        Self::InvalidConfiguration {
138            message: message.into(),
139        }
140    }
141
142    /// Create a new timeout error
143    pub fn timeout(timeout_ms: u64) -> Self {
144        Self::Timeout { timeout_ms }
145    }
146
147    /// Create a new invalid URI error
148    pub fn invalid_uri<S: Into<String>>(uri: S) -> Self {
149        Self::InvalidUri { uri: uri.into() }
150    }
151
152    /// Create a new server not found error
153    pub fn server_not_found<S: Into<String>>(server_id: S) -> Self {
154        Self::ServerNotFound {
155            server_id: server_id.into(),
156        }
157    }
158
159    /// Create a new feature not supported error
160    pub fn feature_not_supported<S: Into<String>>(feature: S, server_id: S) -> Self {
161        Self::FeatureNotSupported {
162            feature: feature.into(),
163            server_id: server_id.into(),
164        }
165    }
166
167    /// Create a new JSON-RPC error
168    pub fn json_rpc<S: Into<String>>(message: S) -> Self {
169        Self::JsonRpc {
170            message: message.into(),
171        }
172    }
173
174    /// Create a new protocol error
175    pub fn protocol<S: Into<String>>(message: S) -> Self {
176        Self::Communication {
177            message: format!("Protocol error: {}", message.into()),
178        }
179    }
180
181    /// Create a new custom error
182    pub fn custom<S: Into<String>>(message: S) -> Self {
183        Self::Custom {
184            message: message.into(),
185        }
186    }
187
188    /// Check if the error is recoverable
189    pub fn is_recoverable(&self) -> bool {
190        match self {
191            Self::ServerCrash { .. } => true,
192            Self::Communication { .. } => true,
193            Self::Timeout { .. } => true,
194            Self::ServerStartup { .. } => false,
195            Self::ProtocolMismatch { .. } => false,
196            Self::InvalidConfiguration { .. } => false,
197            Self::InvalidUri { .. } => false,
198            Self::ServerNotFound { .. } => false,
199            Self::FeatureNotSupported { .. } => false,
200            Self::JsonRpc { .. } => false,
201            Self::Io(_) => true,
202            Self::Serialization(_) => false,
203            Self::Custom { .. } => false,
204        }
205    }
206
207    /// Get error severity level
208    pub fn severity(&self) -> ErrorSeverity {
209        match self {
210            Self::ServerCrash { .. } => ErrorSeverity::Critical,
211            Self::ServerStartup { .. } => ErrorSeverity::Critical,
212            Self::ProtocolMismatch { .. } => ErrorSeverity::Critical,
213            Self::Communication { .. } => ErrorSeverity::High,
214            Self::Timeout { .. } => ErrorSeverity::Medium,
215            Self::InvalidConfiguration { .. } => ErrorSeverity::High,
216            Self::InvalidUri { .. } => ErrorSeverity::Medium,
217            Self::ServerNotFound { .. } => ErrorSeverity::Medium,
218            Self::FeatureNotSupported { .. } => ErrorSeverity::Low,
219            Self::JsonRpc { .. } => ErrorSeverity::Medium,
220            Self::Io(_) => ErrorSeverity::Medium,
221            Self::Serialization(_) => ErrorSeverity::Medium,
222            Self::Custom { .. } => ErrorSeverity::Medium,
223        }
224    }
225}
226
227/// Error severity levels for logging and handling
228#[derive(Debug, Clone, Copy, PartialEq, Eq)]
229pub enum ErrorSeverity {
230    Low,
231    Medium,
232    High,
233    Critical,
234}
235
236impl fmt::Display for ErrorSeverity {
237    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238        match self {
239            Self::Low => write!(f, "LOW"),
240            Self::Medium => write!(f, "MEDIUM"),
241            Self::High => write!(f, "HIGH"),
242            Self::Critical => write!(f, "CRITICAL"),
243        }
244    }
245}
246
247#[cfg(test)]
248mod tests {
249    use super::*;
250
251    #[test]
252    fn test_error_creation() {
253        let error = LspError::server_startup("Test message");
254        assert!(matches!(error, LspError::ServerStartup { .. }));
255        assert!(!error.is_recoverable());
256        assert_eq!(error.severity(), ErrorSeverity::Critical);
257    }
258
259    #[test]
260    fn test_error_recoverable() {
261        let recoverable = LspError::server_crash("test-server");
262        let non_recoverable = LspError::invalid_configuration("bad config");
263        
264        assert!(recoverable.is_recoverable());
265        assert!(!non_recoverable.is_recoverable());
266    }
267
268    #[test]
269    fn test_error_severity() {
270        let critical = LspError::server_startup("startup failed");
271        let medium = LspError::timeout(5000);
272        let low = LspError::feature_not_supported("hover", "test-server");
273
274        assert_eq!(critical.severity(), ErrorSeverity::Critical);
275        assert_eq!(medium.severity(), ErrorSeverity::Medium);
276        assert_eq!(low.severity(), ErrorSeverity::Low);
277    }
278}