use thiserror::Error;
#[derive(Debug, Error)]
#[allow(clippy::enum_variant_names)]
pub enum Error {
#[error("detrix client already initialized")]
AlreadyInitialized,
#[error("detrix client not initialized")]
NotInitialized,
#[error("wake operation already in progress")]
WakeInProgress,
#[error("lldb-dap not found: {0}")]
LldbNotFound(String),
#[error("failed to start lldb-dap: {0}")]
LldbStartFailed(String),
#[error("lldb-dap process exited unexpectedly")]
LldbProcessDied,
#[error("daemon not reachable at {url}: {message}")]
DaemonUnreachable { url: String, message: String },
#[error("failed to register with daemon: {0}")]
RegistrationFailed(String),
#[error("failed to start control plane: {0}")]
ControlPlaneError(String),
#[error("HTTP request failed: {0}")]
HttpError(#[from] reqwest::Error),
#[error("IO error: {0}")]
IoError(#[from] std::io::Error),
#[error("JSON error: {0}")]
JsonError(#[from] serde_json::Error),
#[error("timeout: {0}")]
Timeout(String),
#[error("port bind failed: {0}")]
PortBindError(String),
#[error("configuration error: {0}")]
ConfigError(String),
}
pub type Result<T> = std::result::Result<T, Error>;
impl<T> From<std::sync::PoisonError<T>> for Error {
fn from(_: std::sync::PoisonError<T>) -> Self {
Error::ControlPlaneError("lock poisoned".to_string())
}
}
pub(crate) trait ResultExt<T> {
fn control_plane(self, msg: &str) -> Result<T>;
fn lldb(self, msg: &str) -> Result<T>;
fn port_bind(self, msg: &str) -> Result<T>;
fn config(self, msg: &str) -> Result<T>;
fn registration(self, msg: &str) -> Result<T>;
}
impl<T, E: std::fmt::Display> ResultExt<T> for std::result::Result<T, E> {
fn control_plane(self, msg: &str) -> Result<T> {
self.map_err(|e| Error::ControlPlaneError(format!("{}: {}", msg, e)))
}
fn lldb(self, msg: &str) -> Result<T> {
self.map_err(|e| Error::LldbStartFailed(format!("{}: {}", msg, e)))
}
fn port_bind(self, msg: &str) -> Result<T> {
self.map_err(|e| Error::PortBindError(format!("{}: {}", msg, e)))
}
fn config(self, msg: &str) -> Result<T> {
self.map_err(|e| Error::ConfigError(format!("{}: {}", msg, e)))
}
fn registration(self, msg: &str) -> Result<T> {
self.map_err(|e| Error::RegistrationFailed(format!("{}: {}", msg, e)))
}
}
pub(crate) trait ReqwestResultExt<T> {
fn daemon_unreachable(self, url: &str) -> Result<T>;
fn registration_context(self) -> Result<T>;
}
impl<T> ReqwestResultExt<T> for std::result::Result<T, reqwest::Error> {
fn daemon_unreachable(self, url: &str) -> Result<T> {
self.map_err(|e| Error::DaemonUnreachable {
url: url.to_string(),
message: e.to_string(),
})
}
fn registration_context(self) -> Result<T> {
self.map_err(|e| {
use std::error::Error as StdError;
let mut details = format!("{}", e);
let err: &dyn StdError = &e;
if let Some(source) = err.source() {
details.push_str(&format!(" (caused by: {})", source));
if let Some(inner) = source.source() {
details.push_str(&format!(" (inner: {})", inner));
}
}
Error::RegistrationFailed(details)
})
}
}