1use std::fmt;
4use thiserror::Error;
5
6#[derive(Error, Debug)]
8pub enum LspBridgeError {
9 #[error("LSP error: {0}")]
11 Lsp(#[from] LspError),
12
13 #[error("Resource limit exceeded: {0}")]
15 ResourceLimitExceeded(String),
16
17 #[error("Rate limit exceeded: {0}")]
19 RateLimitExceeded(String),
20
21 #[error("Circuit breaker open: {0}")]
23 CircuitBreakerOpen(String),
24
25 #[error("Input validation failed: {0}")]
27 ValidationFailed(String),
28
29 #[error("Security violation: {0}")]
31 SecurityViolation(String),
32
33 #[error("Configuration error: {0}")]
35 Configuration(String),
36
37 #[error("IO error: {0}")]
39 Io(#[from] std::io::Error),
40
41 #[error("JSON error: {0}")]
43 Json(#[from] serde_json::Error),
44
45 #[error("Timeout: {0}")]
47 Timeout(String),
48
49 #[error("Error: {0}")]
51 Generic(String),
52}
53
54pub type Result<T> = std::result::Result<T, LspBridgeError>;
56
57#[derive(Error, Debug)]
59pub enum LspError {
60 #[error("Server startup failed: {message}")]
62 ServerStartup { message: String },
63
64 #[error("Server communication error: {message}")]
66 Communication { message: String },
67
68 #[error("Server crashed: {server_id}")]
70 ServerCrash { server_id: String },
71
72 #[error("Protocol version mismatch: expected {expected}, got {actual}")]
74 ProtocolMismatch { expected: String, actual: String },
75
76 #[error("Invalid server configuration: {message}")]
78 InvalidConfiguration { message: String },
79
80 #[error("Request timed out after {timeout_ms}ms")]
82 Timeout { timeout_ms: u64 },
83
84 #[error("Invalid document URI: {uri}")]
86 InvalidUri { uri: String },
87
88 #[error("Server not found: {server_id}")]
90 ServerNotFound { server_id: String },
91
92 #[error("Feature '{feature}' not supported by server {server_id}")]
94 FeatureNotSupported { feature: String, server_id: String },
95
96 #[error("JSON-RPC error: {message}")]
98 JsonRpc { message: String },
99
100 #[error("IO error: {0}")]
102 Io(#[from] std::io::Error),
103
104 #[error("Serialization error: {0}")]
106 Serialization(#[from] serde_json::Error),
107
108 #[error("Custom error: {message}")]
110 Custom { message: String },
111}
112
113impl LspError {
114 pub fn server_startup<S: Into<String>>(message: S) -> Self {
116 Self::ServerStartup {
117 message: message.into(),
118 }
119 }
120
121 pub fn communication<S: Into<String>>(message: S) -> Self {
123 Self::Communication {
124 message: message.into(),
125 }
126 }
127
128 pub fn server_crash<S: Into<String>>(server_id: S) -> Self {
130 Self::ServerCrash {
131 server_id: server_id.into(),
132 }
133 }
134
135 pub fn invalid_configuration<S: Into<String>>(message: S) -> Self {
137 Self::InvalidConfiguration {
138 message: message.into(),
139 }
140 }
141
142 pub fn timeout(timeout_ms: u64) -> Self {
144 Self::Timeout { timeout_ms }
145 }
146
147 pub fn invalid_uri<S: Into<String>>(uri: S) -> Self {
149 Self::InvalidUri { uri: uri.into() }
150 }
151
152 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 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 pub fn json_rpc<S: Into<String>>(message: S) -> Self {
169 Self::JsonRpc {
170 message: message.into(),
171 }
172 }
173
174 pub fn protocol<S: Into<String>>(message: S) -> Self {
176 Self::Communication {
177 message: format!("Protocol error: {}", message.into()),
178 }
179 }
180
181 pub fn custom<S: Into<String>>(message: S) -> Self {
183 Self::Custom {
184 message: message.into(),
185 }
186 }
187
188 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 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#[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}