use std::time::Duration;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("cannot connect to incusd at {socket}: {source}")]
Connect {
socket: String,
#[source]
source: std::io::Error,
},
#[error("incusd did not answer {method} {path} within {timeout:?}")]
RequestTimeout {
method: String,
path: String,
timeout: Duration,
},
#[error("incusd: {method} {path}: {message} (HTTP {status})")]
Api {
method: String,
path: String,
status: u16,
message: String,
},
#[error(
"{step} stalled: operation {operation} still {status} after {waited:?}{}",
if *.cancelled { " (cancelled)" } else { " (not cancellable; left running on the server)" }
)]
OperationTimeout {
step: String,
operation: String,
status: String,
waited: Duration,
cancelled: bool,
},
#[error("{step} failed: {message}")]
OperationFailed { step: String, message: String },
#[error("sandbox {sandbox} not ready after {waited:?}: {check} ({detail})")]
NotReady {
sandbox: String,
check: String,
detail: String,
waited: Duration,
},
#[error("exec {argv} timed out after {timeout:?} (killed)")]
ExecTimeout { argv: String, timeout: Duration },
#[error("{0} not found")]
NotFound(String),
#[error("sandbox {0} already exists")]
AlreadyExists(String),
#[error("{0}")]
Invalid(String),
#[error("forbidden: {0}")]
Forbidden(String),
#[error("interpolation: {0}")]
Interpolation(String),
#[error("{path}: {message}")]
Parse { path: String, message: String },
#[error("websocket: {0}")]
WebSocket(String),
#[error("protocol: {0}")]
Protocol(String),
#[error("{message}")]
Remote {
code: String,
message: String,
data: serde_json::Value,
},
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
Json(#[from] serde_json::Error),
}
impl Error {
#[doc(hidden)]
pub fn invalid(msg: impl Into<String>) -> Self {
Error::Invalid(msg.into())
}
pub fn is_not_found(&self) -> bool {
matches!(self, Error::Api { status: 404, .. } | Error::NotFound(_))
|| matches!(self, Error::Remote { code, .. } if code == "not_found")
}
pub fn is_conflict(&self) -> bool {
matches!(
self,
Error::Api { status: 409, .. } | Error::AlreadyExists(_)
) || matches!(self, Error::Api { message, .. } if message.contains("already exists"))
|| matches!(self, Error::Remote { code, .. } if code == "already_exists")
}
pub fn is_timeout(&self) -> bool {
matches!(
self,
Error::RequestTimeout { .. } | Error::OperationTimeout { .. }
) || matches!(self, Error::Remote { code, .. } if code == "request_timeout" || code == "operation_timeout")
}
}
pub type Result<T, E = Error> = std::result::Result<T, E>;