Skip to main content

krait/lsp/
error.rs

1use std::fmt;
2use std::time::Duration;
3
4use crate::detect::Language;
5
6/// Errors specific to LSP client operations.
7#[derive(Debug)]
8pub enum LspError {
9    /// The LSP server binary was not found in PATH.
10    ServerNotFound { language: Language, advice: String },
11    /// The initialize handshake failed.
12    InitializeFailed { message: String },
13    /// An operation timed out.
14    Timeout {
15        operation: String,
16        duration: Duration,
17    },
18    /// The LSP server process crashed.
19    ServerCrashed { exit_code: Option<i32> },
20}
21
22impl fmt::Display for LspError {
23    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        match self {
25            Self::ServerNotFound { language, advice } => {
26                write!(f, "LSP server for {language} not found. {advice}")
27            }
28            Self::InitializeFailed { message } => {
29                write!(f, "LSP initialize failed: {message}")
30            }
31            Self::Timeout {
32                operation,
33                duration,
34            } => {
35                write!(f, "LSP {operation} timed out after {duration:.1?}")
36            }
37            Self::ServerCrashed { exit_code } => match exit_code {
38                Some(code) => write!(f, "LSP server crashed with exit code {code}"),
39                None => write!(f, "LSP server crashed (no exit code)"),
40            },
41        }
42    }
43}
44
45impl std::error::Error for LspError {}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    #[test]
52    fn server_not_found_display_includes_advice() {
53        let err = LspError::ServerNotFound {
54            language: Language::Rust,
55            advice: "Install: rustup component add rust-analyzer".to_string(),
56        };
57        let msg = err.to_string();
58        assert!(msg.contains("rust"));
59        assert!(msg.contains("rust-analyzer"));
60    }
61
62    #[test]
63    fn initialize_failed_display() {
64        let err = LspError::InitializeFailed {
65            message: "server returned error".to_string(),
66        };
67        assert!(err.to_string().contains("server returned error"));
68    }
69
70    #[test]
71    fn timeout_display() {
72        let err = LspError::Timeout {
73            operation: "initialize".to_string(),
74            duration: Duration::from_secs(30),
75        };
76        let msg = err.to_string();
77        assert!(msg.contains("initialize"));
78        assert!(msg.contains("30"));
79    }
80
81    #[test]
82    fn server_crashed_with_code() {
83        let err = LspError::ServerCrashed { exit_code: Some(1) };
84        assert!(err.to_string().contains("exit code 1"));
85    }
86
87    #[test]
88    fn server_crashed_no_code() {
89        let err = LspError::ServerCrashed { exit_code: None };
90        assert!(err.to_string().contains("no exit code"));
91    }
92}