1use thiserror::Error;
4
5#[derive(Debug, Error)]
7#[allow(clippy::enum_variant_names)]
8pub enum Error {
9 #[error("detrix client already initialized")]
11 AlreadyInitialized,
12
13 #[error("detrix client not initialized")]
15 NotInitialized,
16
17 #[error("wake operation already in progress")]
19 WakeInProgress,
20
21 #[error("lldb-dap not found: {0}")]
23 LldbNotFound(String),
24
25 #[error("failed to start lldb-dap: {0}")]
27 LldbStartFailed(String),
28
29 #[error("lldb-dap process exited unexpectedly")]
31 LldbProcessDied,
32
33 #[error("daemon not reachable at {url}: {message}")]
35 DaemonUnreachable { url: String, message: String },
36
37 #[error("failed to register with daemon: {0}")]
39 RegistrationFailed(String),
40
41 #[error("failed to start control plane: {0}")]
43 ControlPlaneError(String),
44
45 #[error("HTTP request failed: {0}")]
47 HttpError(#[from] reqwest::Error),
48
49 #[error("IO error: {0}")]
51 IoError(#[from] std::io::Error),
52
53 #[error("JSON error: {0}")]
55 JsonError(#[from] serde_json::Error),
56
57 #[error("timeout: {0}")]
59 Timeout(String),
60
61 #[error("port bind failed: {0}")]
63 PortBindError(String),
64
65 #[error("configuration error: {0}")]
67 ConfigError(String),
68}
69
70pub type Result<T> = std::result::Result<T, Error>;
72
73impl<T> From<std::sync::PoisonError<T>> for Error {
74 fn from(_: std::sync::PoisonError<T>) -> Self {
75 Error::ControlPlaneError("lock poisoned".to_string())
76 }
77}
78
79pub(crate) trait ResultExt<T> {
81 fn control_plane(self, msg: &str) -> Result<T>;
82 fn lldb(self, msg: &str) -> Result<T>;
83 fn port_bind(self, msg: &str) -> Result<T>;
84 fn config(self, msg: &str) -> Result<T>;
85 fn registration(self, msg: &str) -> Result<T>;
86}
87
88impl<T, E: std::fmt::Display> ResultExt<T> for std::result::Result<T, E> {
89 fn control_plane(self, msg: &str) -> Result<T> {
90 self.map_err(|e| Error::ControlPlaneError(format!("{}: {}", msg, e)))
91 }
92
93 fn lldb(self, msg: &str) -> Result<T> {
94 self.map_err(|e| Error::LldbStartFailed(format!("{}: {}", msg, e)))
95 }
96
97 fn port_bind(self, msg: &str) -> Result<T> {
98 self.map_err(|e| Error::PortBindError(format!("{}: {}", msg, e)))
99 }
100
101 fn config(self, msg: &str) -> Result<T> {
102 self.map_err(|e| Error::ConfigError(format!("{}: {}", msg, e)))
103 }
104
105 fn registration(self, msg: &str) -> Result<T> {
106 self.map_err(|e| Error::RegistrationFailed(format!("{}: {}", msg, e)))
107 }
108}
109
110pub(crate) trait ReqwestResultExt<T> {
112 fn daemon_unreachable(self, url: &str) -> Result<T>;
113 fn registration_context(self) -> Result<T>;
114}
115
116impl<T> ReqwestResultExt<T> for std::result::Result<T, reqwest::Error> {
117 fn daemon_unreachable(self, url: &str) -> Result<T> {
118 self.map_err(|e| Error::DaemonUnreachable {
119 url: url.to_string(),
120 message: e.to_string(),
121 })
122 }
123
124 fn registration_context(self) -> Result<T> {
125 self.map_err(|e| {
126 use std::error::Error as StdError;
127 let mut details = format!("{}", e);
128 let err: &dyn StdError = &e;
129 if let Some(source) = err.source() {
130 details.push_str(&format!(" (caused by: {})", source));
131 if let Some(inner) = source.source() {
132 details.push_str(&format!(" (inner: {})", inner));
133 }
134 }
135 Error::RegistrationFailed(details)
136 })
137 }
138}