use std::time::Duration;
use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ErrorKind {
Io,
Json,
Protocol,
Qmp,
Disconnected,
Timeout,
Cancelled,
Policy,
EventLagged,
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum Error {
#[error("I/O error: {source}")]
Io {
#[from]
source: std::io::Error,
},
#[error("JSON error: {source}")]
Json {
#[from]
source: serde_json::Error,
},
#[error("QMP protocol error: {message}")]
Protocol {
message: String,
},
#[error("QMP command failed: {class}: {desc}")]
Qmp {
command: String,
class: String,
desc: String,
},
#[error("QMP connection closed")]
Disconnected,
#[error("QMP command timed out after {timeout:?}")]
Timeout {
timeout: Duration,
},
#[error("QMP command cancelled")]
Cancelled,
#[error("operation rejected by policy: {command}")]
PolicyViolation {
command: String,
reason: String,
},
#[error("event stream lagged behind and dropped {missed} events")]
EventLagged {
missed: usize,
},
}
impl Error {
#[must_use]
pub fn kind(&self) -> ErrorKind {
match self {
Self::Io { .. } => ErrorKind::Io,
Self::Json { .. } => ErrorKind::Json,
Self::Protocol { .. } => ErrorKind::Protocol,
Self::Qmp { .. } => ErrorKind::Qmp,
Self::Disconnected => ErrorKind::Disconnected,
Self::Timeout { .. } => ErrorKind::Timeout,
Self::Cancelled => ErrorKind::Cancelled,
Self::PolicyViolation { .. } => ErrorKind::Policy,
Self::EventLagged { .. } => ErrorKind::EventLagged,
}
}
#[must_use]
pub fn is_retryable(&self) -> bool {
matches!(
self.kind(),
ErrorKind::Io | ErrorKind::Disconnected | ErrorKind::Timeout
)
}
pub(crate) fn protocol(message: impl Into<String>) -> Self {
Self::Protocol {
message: message.into(),
}
}
pub(crate) fn qmp(
command: impl Into<String>,
class: impl Into<String>,
desc: impl Into<String>,
) -> Self {
Self::Qmp {
command: command.into(),
class: class.into(),
desc: desc.into(),
}
}
pub(crate) fn policy(command: impl Into<String>, reason: impl Into<String>) -> Self {
Self::PolicyViolation {
command: command.into(),
reason: reason.into(),
}
}
pub(crate) fn clone_for_task(&self) -> Self {
match self {
Self::Io { .. } => Self::Disconnected,
Self::Json { .. } => Self::Disconnected,
Self::Protocol { message } => Self::Protocol {
message: message.clone(),
},
Self::Qmp {
command,
class,
desc,
} => Self::Qmp {
command: command.clone(),
class: class.clone(),
desc: desc.clone(),
},
Self::Disconnected => Self::Disconnected,
Self::Timeout { timeout } => Self::Timeout { timeout: *timeout },
Self::Cancelled => Self::Cancelled,
Self::PolicyViolation { command, reason } => Self::PolicyViolation {
command: command.clone(),
reason: reason.clone(),
},
Self::EventLagged { missed } => Self::EventLagged { missed: *missed },
}
}
}