use std::{error::Error as StdError, fmt};
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ErrorKind {
InvalidArgument,
NotReady,
BackendUnavailable,
BackendFailure,
UnsupportedBackendVersion,
NoSuitableDevice,
MissingCapability,
ResourceExhausted,
FailedPrecondition,
OutOfRange,
NotFound,
AlreadyExists,
Aborted,
CallbackFailure,
CheckpointCorrupt,
DataLoss,
Internal,
Io,
}
pub struct Error {
inner: Box<ErrorData>,
}
struct ErrorData {
kind: ErrorKind,
message: String,
source: Option<Box<dyn StdError + Send + Sync + 'static>>,
}
impl Error {
fn new(data: ErrorData) -> Self {
Self {
inner: Box::new(data),
}
}
pub const fn kind(&self) -> ErrorKind {
self.inner.kind
}
pub fn message(&self) -> &str {
&self.inner.message
}
pub fn callback(message: impl Into<String>) -> Self {
Self::new(ErrorData {
kind: ErrorKind::CallbackFailure,
message: message.into(),
source: None,
})
}
pub(crate) fn backend_unavailable<E>(backend: &'static str, source: E) -> Self
where
E: StdError + Send + Sync + 'static,
{
Self::new(ErrorData {
kind: ErrorKind::BackendUnavailable,
message: format!("{backend} backend is unavailable"),
source: Some(Box::new(source)),
})
}
pub(crate) fn invalid_argument(message: impl Into<String>) -> Self {
Self::new(ErrorData {
kind: ErrorKind::InvalidArgument,
message: message.into(),
source: None,
})
}
pub(crate) fn not_ready(message: impl Into<String>) -> Self {
Self::new(ErrorData {
kind: ErrorKind::NotReady,
message: message.into(),
source: None,
})
}
pub(crate) fn backend_failure<E>(
backend: &'static str,
operation: &'static str,
source: E,
) -> Self
where
E: StdError + Send + Sync + 'static,
{
Self::new(ErrorData {
kind: ErrorKind::BackendFailure,
message: format!("{backend} {operation} failed"),
source: Some(Box::new(source)),
})
}
pub(crate) fn unsupported_backend_version(
backend: &'static str,
required: &'static str,
available: String,
) -> Self {
Self::new(ErrorData {
kind: ErrorKind::UnsupportedBackendVersion,
message: format!(
"{backend} {required} or newer is required; the loader provides {available}"
),
source: None,
})
}
pub(crate) fn no_suitable_device(message: impl Into<String>) -> Self {
Self::new(ErrorData {
kind: ErrorKind::NoSuitableDevice,
message: message.into(),
source: None,
})
}
pub(crate) fn missing_capability(message: impl Into<String>) -> Self {
Self::new(ErrorData {
kind: ErrorKind::MissingCapability,
message: message.into(),
source: None,
})
}
pub(crate) fn resource_exhausted(message: impl Into<String>) -> Self {
Self::new(ErrorData {
kind: ErrorKind::ResourceExhausted,
message: message.into(),
source: None,
})
}
pub(crate) fn failed_precondition(message: impl Into<String>) -> Self {
Self::new(ErrorData {
kind: ErrorKind::FailedPrecondition,
message: message.into(),
source: None,
})
}
pub(crate) fn out_of_range(message: impl Into<String>) -> Self {
Self::new(ErrorData {
kind: ErrorKind::OutOfRange,
message: message.into(),
source: None,
})
}
pub(crate) fn not_found(message: impl Into<String>) -> Self {
Self::new(ErrorData {
kind: ErrorKind::NotFound,
message: message.into(),
source: None,
})
}
pub(crate) fn already_exists(message: impl Into<String>) -> Self {
Self::new(ErrorData {
kind: ErrorKind::AlreadyExists,
message: message.into(),
source: None,
})
}
pub(crate) fn internal(message: impl Into<String>) -> Self {
Self::new(ErrorData {
kind: ErrorKind::Internal,
message: message.into(),
source: None,
})
}
pub(crate) fn checkpoint_corrupt(message: impl Into<String>) -> Self {
Self::new(ErrorData {
kind: ErrorKind::CheckpointCorrupt,
message: format!("corrupt .oam: {}", message.into()),
source: None,
})
}
pub(crate) fn data_loss(message: impl Into<String>) -> Self {
Self::new(ErrorData {
kind: ErrorKind::DataLoss,
message: message.into(),
source: None,
})
}
pub(crate) fn io(operation: &'static str, source: std::io::Error) -> Self {
Self::new(ErrorData {
kind: ErrorKind::Io,
message: format!("{operation} failed"),
source: Some(Box::new(source)),
})
}
}
impl fmt::Debug for Error {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("Error")
.field("kind", &self.inner.kind)
.field("message", &self.inner.message)
.field("source", &self.inner.source)
.finish()
}
}
impl fmt::Display for Error {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.inner.message)
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
self
.inner
.source
.as_deref()
.map(|source| source as &(dyn StdError + 'static))
}
}
pub type Result<T> = std::result::Result<T, Error>;