Skip to main content

dscode_dap/
error.rs

1use thiserror::Error;
2
3/// Errors that can occur in Debug Adapter Protocol operations.
4#[derive(Error, Debug)]
5pub enum DapError {
6    /// Failed to spawn a debug adapter process.
7    #[error("Debug adapter spawn failed for {session_id}: {reason}")]
8    SpawnFailed { session_id: String, reason: String },
9    /// The debug adapter process exited unexpectedly.
10    #[error("Debug adapter crashed: {session_id}")]
11    Crashed { session_id: String },
12    /// An invalid state transition was attempted on the debug adapter.
13    #[error("Invalid state transition: {from_state} -> {to_state}")]
14    InvalidTransition { from_state: String, to_state: String },
15    /// A DAP request exceeded its timeout.
16    #[error("DAP request timed out after {timeout_secs}s")]
17    RequestTimeout { timeout_secs: u64 },
18    /// No debug adapter is registered for the given session.
19    #[error("No debug adapter found for session: {0}")]
20    NotRegistered(String),
21    /// An I/O error occurred during DAP communication.
22    #[error("DAP I/O error: {0}")]
23    Io(String),
24    /// A DAP protocol error (malformed messages, unexpected responses).
25    #[error("DAP protocol error: {0}")]
26    Protocol(String),
27}
28
29impl From<DapError> for String {
30    fn from(err: DapError) -> Self {
31        err.to_string()
32    }
33}
34
35#[cfg(test)]
36mod tests {
37    use super::*;
38
39    #[test]
40    fn test_dap_error_spawn_failed_display() {
41        let err = DapError::SpawnFailed {
42            session_id: "sess-1".to_string(),
43            reason: "command not found".to_string(),
44        };
45        let msg = err.to_string();
46        assert!(msg.contains("spawn failed"), "Display should contain 'spawn failed'");
47        assert!(msg.contains("sess-1"), "Display should contain session id");
48        assert!(msg.contains("command not found"), "Display should contain reason");
49    }
50
51    #[test]
52    fn test_dap_error_crashed_display() {
53        let err = DapError::Crashed { session_id: "sess-2".to_string() };
54        let msg = err.to_string();
55        assert!(msg.contains("crashed"), "Display should contain 'crashed'");
56        assert!(msg.contains("sess-2"), "Display should contain session id");
57    }
58
59    #[test]
60    fn test_dap_error_invalid_transition_display() {
61        let err = DapError::InvalidTransition {
62            from_state: "Stopped".to_string(),
63            to_state: "Running".to_string(),
64        };
65        let msg = err.to_string();
66        assert!(msg.contains("Invalid state transition"), "Display should contain transition label");
67        assert!(msg.contains("Stopped"), "Display should contain from_state");
68        assert!(msg.contains("Running"), "Display should contain to_state");
69    }
70
71    #[test]
72    fn test_dap_error_request_timeout_display() {
73        let err = DapError::RequestTimeout { timeout_secs: 30 };
74        let msg = err.to_string();
75        assert!(msg.contains("timed out"), "Display should contain 'timed out'");
76        assert!(msg.contains("30"), "Display should contain timeout duration");
77    }
78
79    #[test]
80    fn test_dap_error_not_registered_display() {
81        let err = DapError::NotRegistered("sess-3".to_string());
82        let msg = err.to_string();
83        assert!(msg.contains("No debug adapter found"), "Display should contain 'No debug adapter found'");
84        assert!(msg.contains("sess-3"), "Display should contain session id");
85    }
86
87    #[test]
88    fn test_dap_error_io_display() {
89        let err = DapError::Io("connection reset".to_string());
90        let msg = err.to_string();
91        assert!(msg.contains("I/O error"), "Display should contain 'I/O error'");
92        assert!(msg.contains("connection reset"), "Display should contain the message");
93    }
94
95    #[test]
96    fn test_dap_error_protocol_display() {
97        let err = DapError::Protocol("malformed header".to_string());
98        let msg = err.to_string();
99        assert!(msg.contains("protocol error"), "Display should contain 'protocol error'");
100        assert!(msg.contains("malformed header"), "Display should contain the message");
101    }
102
103    #[test]
104    fn test_dap_error_into_string() {
105        let err = DapError::SpawnFailed {
106            session_id: "s".to_string(),
107            reason: "r".to_string(),
108        };
109        let s: String = err.into();
110        assert!(s.contains("spawn failed"), "Into<String> should produce the Display output");
111    }
112
113    #[test]
114    fn test_dap_error_debug_format() {
115        let err = DapError::Crashed { session_id: "s".to_string() };
116        let debug = format!("{:?}", err);
117        assert!(debug.contains("Crashed"), "Debug format should contain variant name");
118    }
119}