use thiserror::Error;
#[derive(Debug, Error)]
pub enum CoreError {
#[error("Cannot connect to controller at {url}: {reason}")]
ConnectionFailed { url: String, reason: String },
#[error("Authentication failed: {message}")]
AuthenticationFailed { message: String },
#[error("Controller disconnected")]
ControllerDisconnected,
#[error("Controller connection timed out after {timeout_secs}s")]
Timeout { timeout_secs: u64 },
#[error("Device not found: {identifier}")]
DeviceNotFound { identifier: String },
#[error("Client not found: {identifier}")]
ClientNotFound { identifier: String },
#[error("Network not found: {identifier}")]
NetworkNotFound { identifier: String },
#[error("Site not found: {name}")]
SiteNotFound { name: String },
#[error("Entity not found: {entity_type} with id {identifier}")]
NotFound {
entity_type: String,
identifier: String,
},
#[error("Operation not supported: {operation} (requires {required})")]
Unsupported { operation: String, required: String },
#[error("Operation rejected by controller: {message}")]
Rejected { message: String },
#[error("Validation failed: {message}")]
ValidationFailed { message: String },
#[error("Operation failed: {message}")]
OperationFailed { message: String },
#[error("API error: {message}")]
Api {
message: String,
code: Option<String>,
status: Option<u16>,
},
#[error("Configuration error: {message}")]
Config { message: String },
#[error("Internal error: {0}")]
Internal(String),
}
impl From<crate::error::Error> for CoreError {
fn from(err: crate::error::Error) -> Self {
match err {
crate::error::Error::Authentication { message } => {
CoreError::AuthenticationFailed { message }
}
crate::error::Error::TwoFactorRequired => CoreError::AuthenticationFailed {
message: "Two-factor authentication token required".into(),
},
crate::error::Error::SessionExpired => CoreError::AuthenticationFailed {
message: "Session expired -- re-authentication required".into(),
},
crate::error::Error::InvalidApiKey => CoreError::AuthenticationFailed {
message: "Invalid API key".into(),
},
crate::error::Error::WrongAuthStrategy { expected, got } => {
CoreError::AuthenticationFailed {
message: format!("Wrong auth strategy: expected {expected}, got {got}"),
}
}
crate::error::Error::Transport(ref e) => {
if e.is_timeout() {
CoreError::Timeout { timeout_secs: 0 }
} else if e.is_connect() {
CoreError::ConnectionFailed {
url: e
.url()
.map_or_else(|| "<unknown>".into(), ToString::to_string),
reason: e.to_string(),
}
} else if e.status().map(|s| s.as_u16()) == Some(404) {
CoreError::NotFound {
entity_type: "resource".into(),
identifier: e.url().map(|u| u.path().to_string()).unwrap_or_default(),
}
} else {
CoreError::Api {
message: e.to_string(),
code: None,
status: e.status().map(|s| s.as_u16()),
}
}
}
crate::error::Error::InvalidUrl(e) => CoreError::Config {
message: format!("Invalid URL: {e}"),
},
crate::error::Error::Timeout { timeout_secs } => CoreError::Timeout { timeout_secs },
crate::error::Error::Tls(msg) => CoreError::ConnectionFailed {
url: String::new(),
reason: format!("TLS error: {msg}"),
},
crate::error::Error::RateLimited { retry_after_secs } => CoreError::Api {
message: format!("Rate limited -- retry after {retry_after_secs}s"),
code: Some("rate_limited".into()),
status: Some(429),
},
crate::error::Error::Integration {
message,
code,
status,
} => CoreError::Api {
message,
code,
status: Some(status),
},
crate::error::Error::LegacyApi { message } => CoreError::Api {
message,
code: None,
status: None,
},
crate::error::Error::WebSocketConnect(reason) => CoreError::ConnectionFailed {
url: String::new(),
reason: format!("WebSocket connection failed: {reason}"),
},
crate::error::Error::WebSocketClosed { code, reason } => CoreError::ConnectionFailed {
url: String::new(),
reason: format!("WebSocket closed (code {code}): {reason}"),
},
crate::error::Error::Deserialization { message, body: _ } => {
CoreError::Internal(format!("Deserialization error: {message}"))
}
crate::error::Error::UnsupportedOperation(op) => CoreError::Unsupported {
operation: op.to_string(),
required: "a newer controller firmware".into(),
},
}
}
}