Skip to main content

spring_lsp/
error.rs

1//! 错误类型定义和错误处理策略
2//!
3//! 本模块定义了 spring-lsp 的错误类型体系和错误处理策略。
4//!
5//! ## 错误分类
6//!
7//! 1. **协议错误** (`ProtocolError`): LSP 协议违规或通信错误
8//! 2. **解析错误** (`ParseError`): TOML 或 Rust 代码语法错误
9//! 3. **验证错误** (`ValidationError`): 配置或代码语义错误
10//! 4. **系统错误** (`SystemError`): 文件 I/O、网络请求等系统级错误
11//!
12//! ## 错误处理策略
13//!
14//! - **协议错误**: 记录日志,返回标准 LSP 错误响应,尝试恢复连接
15//! - **解析错误**: 生成诊断信息,尝试部分解析,缓存错误状态
16//! - **验证错误**: 生成诊断信息,不影响其他文档,提供快速修复
17//! - **系统错误**: 记录日志,使用降级策略(缓存/默认值),显示友好错误
18//!
19//! ## 错误恢复
20//!
21//! - **连接恢复**: 协议错误后尝试重新建立连接
22//! - **部分解析**: 解析错误时尽可能提取有效信息
23//! - **缓存使用**: 系统错误时使用缓存的数据
24//! - **降级策略**: 关键功能失败时使用备用方案
25
26use lsp_types::Url;
27use thiserror::Error;
28
29/// spring-lsp 错误类型
30#[derive(Debug, Error)]
31pub enum Error {
32    // ========== 协议错误 ==========
33    /// LSP 协议错误
34    #[error("LSP protocol error: {0}")]
35    Protocol(#[from] lsp_server::ProtocolError),
36
37    /// 消息发送错误
38    #[error("Failed to send message: {0}")]
39    MessageSend(String),
40
41    /// 消息接收错误
42    #[error("Failed to receive message: {0}")]
43    MessageReceive(String),
44
45    // ========== 解析错误 ==========
46    /// TOML 解析错误
47    #[error("TOML parse error in {uri}: {message}")]
48    TomlParse { uri: String, message: String },
49
50    /// Rust 语法解析错误
51    #[error("Rust parse error in {uri}: {message}")]
52    RustParse { uri: String, message: String },
53
54    /// 环境变量插值语法错误
55    #[error("Invalid environment variable syntax in {uri} at line {line}: {message}")]
56    EnvVarSyntax {
57        uri: String,
58        line: u32,
59        message: String,
60    },
61
62    // ========== 验证错误 ==========
63    /// 配置验证错误
64    #[error("Configuration validation error in {uri}: {message}")]
65    ConfigValidation { uri: String, message: String },
66
67    /// 路由验证错误
68    #[error("Route validation error in {uri}: {message}")]
69    RouteValidation { uri: String, message: String },
70
71    /// 依赖注入验证错误
72    #[error("Dependency injection validation error in {uri}: {message}")]
73    DiValidation { uri: String, message: String },
74
75    // ========== 系统错误 ==========
76    /// Schema 加载错误
77    #[error("Schema load error: {0}")]
78    SchemaLoad(String),
79
80    /// 配置错误
81    #[error("Configuration error: {0}")]
82    Config(String),
83
84    /// 文件 I/O 错误
85    #[error("File I/O error: {0}")]
86    Io(#[from] std::io::Error),
87
88    /// JSON 序列化/反序列化错误
89    #[error("JSON error: {0}")]
90    Json(#[from] serde_json::Error),
91
92    /// HTTP 请求错误
93    #[error("HTTP request error: {0}")]
94    Http(#[from] reqwest::Error),
95
96    /// 索引构建错误
97    #[error("Index build error: {0}")]
98    IndexBuild(String),
99
100    /// 其他错误
101    #[error("{0}")]
102    Other(#[from] anyhow::Error),
103}
104
105impl Error {
106    /// 获取错误类别
107    pub fn category(&self) -> ErrorCategory {
108        match self {
109            Error::Protocol(_) | Error::MessageSend(_) | Error::MessageReceive(_) => {
110                ErrorCategory::Protocol
111            }
112            Error::TomlParse { .. } | Error::RustParse { .. } | Error::EnvVarSyntax { .. } => {
113                ErrorCategory::Parse
114            }
115            Error::ConfigValidation { .. }
116            | Error::RouteValidation { .. }
117            | Error::DiValidation { .. } => ErrorCategory::Validation,
118            Error::SchemaLoad(_)
119            | Error::Config(_)
120            | Error::Io(_)
121            | Error::Json(_)
122            | Error::Http(_)
123            | Error::IndexBuild(_)
124            | Error::Other(_) => ErrorCategory::System,
125        }
126    }
127
128    /// 判断错误是否可恢复
129    pub fn is_recoverable(&self) -> bool {
130        match self {
131            // 协议错误可能可以恢复
132            Error::Protocol(_) => true,
133            Error::MessageSend(_) | Error::MessageReceive(_) => true,
134
135            // 解析错误可以部分恢复(提供有限功能)
136            Error::TomlParse { .. } | Error::RustParse { .. } | Error::EnvVarSyntax { .. } => true,
137
138            // 验证错误不影响服务器运行
139            Error::ConfigValidation { .. }
140            | Error::RouteValidation { .. }
141            | Error::DiValidation { .. } => true,
142
143            // 系统错误部分可恢复
144            Error::SchemaLoad(_) => true, // 可以使用备用 Schema
145            Error::Config(_) => false,    // 配置错误不可恢复
146            Error::Http(_) => true,       // 可以使用缓存
147            Error::IndexBuild(_) => true, // 可以跳过索引构建
148            Error::Io(_) => false,        // I/O 错误通常不可恢复
149            Error::Json(_) => false,      // JSON 错误通常不可恢复
150            Error::Other(_) => false,     // 未知错误默认不可恢复
151        }
152    }
153
154    /// 获取错误的严重程度
155    pub fn severity(&self) -> ErrorSeverity {
156        match self {
157            // 协议错误是严重的,可能导致连接中断
158            Error::Protocol(_) => ErrorSeverity::Error,
159            Error::MessageSend(_) | Error::MessageReceive(_) => ErrorSeverity::Error,
160
161            // 解析错误是警告,不影响其他功能
162            Error::TomlParse { .. } | Error::RustParse { .. } => ErrorSeverity::Warning,
163            Error::EnvVarSyntax { .. } => ErrorSeverity::Warning,
164
165            // 验证错误是信息,只影响诊断
166            Error::ConfigValidation { .. }
167            | Error::RouteValidation { .. }
168            | Error::DiValidation { .. } => ErrorSeverity::Info,
169
170            // 系统错误根据类型判断
171            Error::SchemaLoad(_) => ErrorSeverity::Warning, // 可以使用备用 Schema
172            Error::Config(_) => ErrorSeverity::Error,       // 配置错误是严重的
173            Error::Http(_) => ErrorSeverity::Warning,       // 可以使用缓存
174            Error::IndexBuild(_) => ErrorSeverity::Warning, // 可以跳过索引
175            Error::Io(_) => ErrorSeverity::Error,           // I/O 错误是严重的
176            Error::Json(_) => ErrorSeverity::Error,         // JSON 错误是严重的
177            Error::Other(_) => ErrorSeverity::Error,        // 未知错误默认严重
178        }
179    }
180
181    /// 获取错误的文档 URI(如果有)
182    pub fn document_uri(&self) -> Option<&str> {
183        match self {
184            Error::TomlParse { uri, .. }
185            | Error::RustParse { uri, .. }
186            | Error::EnvVarSyntax { uri, .. }
187            | Error::ConfigValidation { uri, .. }
188            | Error::RouteValidation { uri, .. }
189            | Error::DiValidation { uri, .. } => Some(uri),
190            _ => None,
191        }
192    }
193}
194
195/// 错误类别
196#[derive(Debug, Clone, Copy, PartialEq, Eq)]
197pub enum ErrorCategory {
198    /// 协议错误
199    Protocol,
200    /// 解析错误
201    Parse,
202    /// 验证错误
203    Validation,
204    /// 系统错误
205    System,
206}
207
208/// 错误严重程度
209#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
210pub enum ErrorSeverity {
211    /// 信息
212    Info,
213    /// 警告
214    Warning,
215    /// 错误
216    Error,
217}
218
219/// 错误处理器
220///
221/// 提供统一的错误处理和恢复策略
222pub struct ErrorHandler {
223    /// 是否启用详细日志
224    verbose: bool,
225}
226
227impl ErrorHandler {
228    /// 创建新的错误处理器
229    pub fn new(verbose: bool) -> Self {
230        Self { verbose }
231    }
232
233    /// 处理错误
234    ///
235    /// 根据错误类型执行相应的处理策略:
236    /// - 记录日志
237    /// - 尝试恢复
238    /// - 返回降级结果
239    pub fn handle(&self, error: &Error) -> ErrorHandlingResult {
240        // 记录错误日志
241        self.log_error(error);
242
243        // 根据错误类别执行处理策略
244        match error.category() {
245            ErrorCategory::Protocol => self.handle_protocol_error(error),
246            ErrorCategory::Parse => self.handle_parse_error(error),
247            ErrorCategory::Validation => self.handle_validation_error(error),
248            ErrorCategory::System => self.handle_system_error(error),
249        }
250    }
251
252    /// 记录错误日志
253    fn log_error(&self, error: &Error) {
254        match error.severity() {
255            ErrorSeverity::Error => {
256                if self.verbose {
257                    tracing::error!("{:?}", error);
258                } else {
259                    tracing::error!("{}", error);
260                }
261            }
262            ErrorSeverity::Warning => {
263                if self.verbose {
264                    tracing::warn!("{:?}", error);
265                } else {
266                    tracing::warn!("{}", error);
267                }
268            }
269            ErrorSeverity::Info => {
270                if self.verbose {
271                    tracing::info!("{:?}", error);
272                } else {
273                    tracing::info!("{}", error);
274                }
275            }
276        }
277    }
278
279    /// 处理协议错误
280    fn handle_protocol_error(&self, error: &Error) -> ErrorHandlingResult {
281        tracing::error!("Protocol error occurred: {}", error);
282
283        ErrorHandlingResult {
284            action: RecoveryAction::RetryConnection,
285            fallback: None,
286            notify_client: true,
287        }
288    }
289
290    /// 处理解析错误
291    fn handle_parse_error(&self, error: &Error) -> ErrorHandlingResult {
292        tracing::warn!("Parse error occurred: {}", error);
293
294        ErrorHandlingResult {
295            action: RecoveryAction::PartialParse,
296            fallback: None,
297            notify_client: true,
298        }
299    }
300
301    /// 处理验证错误
302    fn handle_validation_error(&self, error: &Error) -> ErrorHandlingResult {
303        tracing::info!("Validation error occurred: {}", error);
304
305        ErrorHandlingResult {
306            action: RecoveryAction::GenerateDiagnostic,
307            fallback: None,
308            notify_client: false, // 验证错误通过诊断通知,不需要额外通知
309        }
310    }
311
312    /// 处理系统错误
313    fn handle_system_error(&self, error: &Error) -> ErrorHandlingResult {
314        tracing::error!("System error occurred: {}", error);
315
316        let (action, fallback) = match error {
317            Error::SchemaLoad(_) => (RecoveryAction::UseFallback, Some("builtin-schema")),
318            Error::Http(_) => (RecoveryAction::UseCache, None),
319            Error::IndexBuild(_) => (RecoveryAction::SkipOperation, None),
320            _ => (RecoveryAction::Abort, None),
321        };
322
323        ErrorHandlingResult {
324            action,
325            fallback: fallback.map(String::from),
326            notify_client: true,
327        }
328    }
329}
330
331/// 错误处理结果
332#[derive(Debug)]
333pub struct ErrorHandlingResult {
334    /// 恢复动作
335    pub action: RecoveryAction,
336    /// 降级方案(如果有)
337    pub fallback: Option<String>,
338    /// 是否通知客户端
339    pub notify_client: bool,
340}
341
342/// 恢复动作
343#[derive(Debug, Clone, Copy, PartialEq, Eq)]
344pub enum RecoveryAction {
345    /// 重试连接
346    RetryConnection,
347    /// 部分解析
348    PartialParse,
349    /// 生成诊断
350    GenerateDiagnostic,
351    /// 使用降级方案
352    UseFallback,
353    /// 使用缓存
354    UseCache,
355    /// 跳过操作
356    SkipOperation,
357    /// 中止
358    Abort,
359}
360
361/// Result 类型别名
362pub type Result<T> = std::result::Result<T, Error>;
363
364/// 创建 TOML 解析错误
365pub fn toml_parse_error(uri: &Url, message: impl Into<String>) -> Error {
366    Error::TomlParse {
367        uri: uri.to_string(),
368        message: message.into(),
369    }
370}
371
372/// 创建 Rust 解析错误
373pub fn rust_parse_error(uri: &Url, message: impl Into<String>) -> Error {
374    Error::RustParse {
375        uri: uri.to_string(),
376        message: message.into(),
377    }
378}
379
380/// 创建环境变量语法错误
381pub fn env_var_syntax_error(uri: &Url, line: u32, message: impl Into<String>) -> Error {
382    Error::EnvVarSyntax {
383        uri: uri.to_string(),
384        line,
385        message: message.into(),
386    }
387}
388
389/// 创建配置验证错误
390pub fn config_validation_error(uri: &Url, message: impl Into<String>) -> Error {
391    Error::ConfigValidation {
392        uri: uri.to_string(),
393        message: message.into(),
394    }
395}
396
397/// 创建路由验证错误
398pub fn route_validation_error(uri: &Url, message: impl Into<String>) -> Error {
399    Error::RouteValidation {
400        uri: uri.to_string(),
401        message: message.into(),
402    }
403}
404
405/// 创建依赖注入验证错误
406pub fn di_validation_error(uri: &Url, message: impl Into<String>) -> Error {
407    Error::DiValidation {
408        uri: uri.to_string(),
409        message: message.into(),
410    }
411}
412
413#[cfg(test)]
414mod tests {
415    use super::*;
416
417    #[test]
418    fn test_error_category() {
419        // 使用消息发送错误代替协议错误(更容易构造)
420        let protocol_err = Error::MessageSend("test error".to_string());
421        assert_eq!(protocol_err.category(), ErrorCategory::Protocol);
422
423        let parse_err = Error::TomlParse {
424            uri: "file:///test.toml".to_string(),
425            message: "syntax error".to_string(),
426        };
427        assert_eq!(parse_err.category(), ErrorCategory::Parse);
428
429        let validation_err = Error::ConfigValidation {
430            uri: "file:///test.toml".to_string(),
431            message: "invalid config".to_string(),
432        };
433        assert_eq!(validation_err.category(), ErrorCategory::Validation);
434
435        let system_err = Error::SchemaLoad("failed to load".to_string());
436        assert_eq!(system_err.category(), ErrorCategory::System);
437    }
438
439    #[test]
440    fn test_error_recoverability() {
441        let protocol_err = Error::MessageSend("test error".to_string());
442        assert!(protocol_err.is_recoverable());
443
444        let parse_err = Error::TomlParse {
445            uri: "file:///test.toml".to_string(),
446            message: "syntax error".to_string(),
447        };
448        assert!(parse_err.is_recoverable());
449
450        let io_err = Error::Io(std::io::Error::new(
451            std::io::ErrorKind::NotFound,
452            "file not found",
453        ));
454        assert!(!io_err.is_recoverable());
455    }
456
457    #[test]
458    fn test_error_severity() {
459        let protocol_err = Error::MessageSend("test error".to_string());
460        assert_eq!(protocol_err.severity(), ErrorSeverity::Error);
461
462        let parse_err = Error::TomlParse {
463            uri: "file:///test.toml".to_string(),
464            message: "syntax error".to_string(),
465        };
466        assert_eq!(parse_err.severity(), ErrorSeverity::Warning);
467
468        let validation_err = Error::ConfigValidation {
469            uri: "file:///test.toml".to_string(),
470            message: "invalid config".to_string(),
471        };
472        assert_eq!(validation_err.severity(), ErrorSeverity::Info);
473    }
474
475    #[test]
476    fn test_error_document_uri() {
477        let parse_err = Error::TomlParse {
478            uri: "file:///test.toml".to_string(),
479            message: "syntax error".to_string(),
480        };
481        assert_eq!(parse_err.document_uri(), Some("file:///test.toml"));
482
483        let system_err = Error::SchemaLoad("failed".to_string());
484        assert_eq!(system_err.document_uri(), None);
485    }
486
487    #[test]
488    fn test_error_handler() {
489        let handler = ErrorHandler::new(false);
490
491        // 测试协议错误处理
492        let protocol_err = Error::MessageSend("test error".to_string());
493        let result = handler.handle(&protocol_err);
494        assert_eq!(result.action, RecoveryAction::RetryConnection);
495        assert!(result.notify_client);
496
497        // 测试解析错误处理
498        let parse_err = Error::TomlParse {
499            uri: "file:///test.toml".to_string(),
500            message: "syntax error".to_string(),
501        };
502        let result = handler.handle(&parse_err);
503        assert_eq!(result.action, RecoveryAction::PartialParse);
504        assert!(result.notify_client);
505
506        // 测试验证错误处理
507        let validation_err = Error::ConfigValidation {
508            uri: "file:///test.toml".to_string(),
509            message: "invalid config".to_string(),
510        };
511        let result = handler.handle(&validation_err);
512        assert_eq!(result.action, RecoveryAction::GenerateDiagnostic);
513        assert!(!result.notify_client);
514
515        // 测试系统错误处理(Schema 加载失败)
516        let schema_err = Error::SchemaLoad("failed".to_string());
517        let result = handler.handle(&schema_err);
518        assert_eq!(result.action, RecoveryAction::UseFallback);
519        assert_eq!(result.fallback, Some("builtin-schema".to_string()));
520        assert!(result.notify_client);
521    }
522
523    #[test]
524    fn test_error_helper_functions() {
525        let uri = Url::parse("file:///test.toml").unwrap();
526
527        let toml_err = toml_parse_error(&uri, "syntax error");
528        assert!(matches!(toml_err, Error::TomlParse { .. }));
529
530        let rust_err = rust_parse_error(&uri, "syntax error");
531        assert!(matches!(rust_err, Error::RustParse { .. }));
532
533        let env_err = env_var_syntax_error(&uri, 10, "invalid syntax");
534        assert!(matches!(env_err, Error::EnvVarSyntax { .. }));
535
536        let config_err = config_validation_error(&uri, "invalid config");
537        assert!(matches!(config_err, Error::ConfigValidation { .. }));
538
539        let route_err = route_validation_error(&uri, "invalid route");
540        assert!(matches!(route_err, Error::RouteValidation { .. }));
541
542        let di_err = di_validation_error(&uri, "invalid injection");
543        assert!(matches!(di_err, Error::DiValidation { .. }));
544    }
545}