1use std::fmt;
2use std::time::Duration;
3
4use crate::detect::Language;
5
6#[derive(Debug)]
8pub enum LspError {
9 ServerNotFound { language: Language, advice: String },
11 InitializeFailed { message: String },
13 Timeout {
15 operation: String,
16 duration: Duration,
17 },
18 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}