use std::{error, fmt, io};
use crate::{options::DurabilityMode, types::ReadVersion};
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
Io(io::Error),
Corruption {
message: String,
},
InvalidFormat {
message: String,
},
UnsupportedFormat {
message: String,
},
CodecUnavailable {
codec: String,
},
Conflict {
message: String,
},
Fenced {
held_epoch: u64,
current_epoch: u64,
},
ReadVersionTooNew {
requested: ReadVersion,
latest: ReadVersion,
},
ReadVersionExpired {
requested: ReadVersion,
oldest_retained: ReadVersion,
},
CheckpointAlreadyExists {
name: String,
},
CheckpointNotFound {
name: String,
},
ReadOnly,
Closed,
RuntimeBusy {
message: String,
},
BucketMissing {
name: String,
},
InvalidOptions {
message: String,
},
Unsupported {
feature: &'static str,
},
UnsupportedBackend {
feature: &'static str,
},
UnsupportedDurability {
requested: DurabilityMode,
},
}
#[derive(Debug, Clone)]
pub(crate) enum ErrorSnapshot {
Io {
message: String,
},
Corruption {
message: String,
},
InvalidFormat {
message: String,
},
UnsupportedFormat {
message: String,
},
CodecUnavailable {
codec: String,
},
Conflict {
message: String,
},
Fenced {
held_epoch: u64,
current_epoch: u64,
},
ReadVersionTooNew {
requested: ReadVersion,
latest: ReadVersion,
},
ReadVersionExpired {
requested: ReadVersion,
oldest_retained: ReadVersion,
},
CheckpointAlreadyExists {
name: String,
},
CheckpointNotFound {
name: String,
},
ReadOnly,
Closed,
RuntimeBusy {
message: String,
},
BucketMissing {
name: String,
},
InvalidOptions {
message: String,
},
Unsupported {
feature: &'static str,
},
UnsupportedBackend {
feature: &'static str,
},
UnsupportedDurability {
requested: DurabilityMode,
},
}
impl ErrorSnapshot {
#[must_use]
pub(crate) fn capture(error: &Error) -> Self {
match error {
Error::Io(error) => Self::Io {
message: error.to_string(),
},
Error::Corruption { message } => Self::Corruption {
message: message.clone(),
},
Error::InvalidFormat { message } => Self::InvalidFormat {
message: message.clone(),
},
Error::UnsupportedFormat { message } => Self::UnsupportedFormat {
message: message.clone(),
},
Error::CodecUnavailable { codec } => Self::CodecUnavailable {
codec: codec.clone(),
},
Error::Conflict { message } => Self::Conflict {
message: message.clone(),
},
Error::Fenced {
held_epoch,
current_epoch,
} => Self::Fenced {
held_epoch: *held_epoch,
current_epoch: *current_epoch,
},
Error::ReadVersionTooNew { requested, latest } => Self::ReadVersionTooNew {
requested: *requested,
latest: *latest,
},
Error::ReadVersionExpired {
requested,
oldest_retained,
} => Self::ReadVersionExpired {
requested: *requested,
oldest_retained: *oldest_retained,
},
Error::CheckpointAlreadyExists { name } => {
Self::CheckpointAlreadyExists { name: name.clone() }
}
Error::CheckpointNotFound { name } => Self::CheckpointNotFound { name: name.clone() },
Error::ReadOnly => Self::ReadOnly,
Error::Closed => Self::Closed,
Error::RuntimeBusy { message } => Self::RuntimeBusy {
message: message.clone(),
},
Error::BucketMissing { name } => Self::BucketMissing { name: name.clone() },
Error::InvalidOptions { message } => Self::InvalidOptions {
message: message.clone(),
},
Error::Unsupported { feature } => Self::Unsupported { feature },
Error::UnsupportedBackend { feature } => Self::UnsupportedBackend { feature },
Error::UnsupportedDurability { requested } => Self::UnsupportedDurability {
requested: *requested,
},
}
}
pub(crate) fn into_error(self) -> Error {
match self {
Self::Io { message } => Error::Io(io::Error::other(message)),
Self::Corruption { message } => Error::Corruption { message },
Self::InvalidFormat { message } => Error::InvalidFormat { message },
Self::UnsupportedFormat { message } => Error::UnsupportedFormat { message },
Self::CodecUnavailable { codec } => Error::CodecUnavailable { codec },
Self::Conflict { message } => Error::Conflict { message },
Self::Fenced {
held_epoch,
current_epoch,
} => Error::Fenced {
held_epoch,
current_epoch,
},
Self::ReadVersionTooNew { requested, latest } => {
Error::ReadVersionTooNew { requested, latest }
}
Self::ReadVersionExpired {
requested,
oldest_retained,
} => Error::ReadVersionExpired {
requested,
oldest_retained,
},
Self::CheckpointAlreadyExists { name } => Error::CheckpointAlreadyExists { name },
Self::CheckpointNotFound { name } => Error::CheckpointNotFound { name },
Self::ReadOnly => Error::ReadOnly,
Self::Closed => Error::Closed,
Self::RuntimeBusy { message } => Error::RuntimeBusy { message },
Self::BucketMissing { name } => Error::BucketMissing { name },
Self::InvalidOptions { message } => Error::InvalidOptions { message },
Self::Unsupported { feature } => Error::Unsupported { feature },
Self::UnsupportedBackend { feature } => Error::UnsupportedBackend { feature },
Self::UnsupportedDurability { requested } => Error::UnsupportedDurability { requested },
}
}
}
impl Error {
#[must_use]
pub const fn unsupported(feature: &'static str) -> Self {
Self::Unsupported { feature }
}
#[must_use]
pub const fn unsupported_backend(feature: &'static str) -> Self {
Self::UnsupportedBackend { feature }
}
#[must_use]
pub const fn unsupported_durability(requested: DurabilityMode) -> Self {
Self::UnsupportedDurability { requested }
}
#[must_use]
pub fn invalid_options(message: impl Into<String>) -> Self {
Self::InvalidOptions {
message: message.into(),
}
}
#[must_use]
pub fn runtime_busy(message: impl Into<String>) -> Self {
Self::RuntimeBusy {
message: message.into(),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(error) => write!(formatter, "io error: {error}"),
Self::Corruption { message } => write!(formatter, "corruption: {message}"),
Self::InvalidFormat { message } => write!(formatter, "invalid format: {message}"),
Self::UnsupportedFormat { message } => {
write!(formatter, "unsupported format: {message}")
}
Self::CodecUnavailable { codec } => write!(formatter, "codec unavailable: {codec}"),
Self::Conflict { message } => write!(formatter, "transaction conflict: {message}"),
Self::Fenced {
held_epoch,
current_epoch,
} => write!(
formatter,
"writer fenced: held epoch {held_epoch} but manifest is at epoch {current_epoch}"
),
Self::ReadVersionTooNew { requested, latest } => write!(
formatter,
"read version {} is newer than latest read version {}",
requested.as_u64(),
latest.as_u64()
),
Self::ReadVersionExpired {
requested,
oldest_retained,
} => write!(
formatter,
"read version {} is older than oldest retained read version {}",
requested.as_u64(),
oldest_retained.as_u64()
),
Self::CheckpointAlreadyExists { name } => {
write!(formatter, "checkpoint already exists: {name}")
}
Self::CheckpointNotFound { name } => write!(formatter, "checkpoint not found: {name}"),
Self::ReadOnly => formatter.write_str("database is read-only"),
Self::Closed => formatter.write_str("database is closed"),
Self::RuntimeBusy { message } => write!(formatter, "runtime busy: {message}"),
Self::BucketMissing { name } => write!(formatter, "bucket is missing: {name}"),
Self::InvalidOptions { message } => write!(formatter, "invalid options: {message}"),
Self::Unsupported { feature } => write!(formatter, "unsupported feature: {feature}"),
Self::UnsupportedBackend { feature } => {
write!(formatter, "unsupported storage backend feature: {feature}")
}
Self::UnsupportedDurability { requested } => {
write!(
formatter,
"unsupported durability mode: {}",
requested.as_str()
)
}
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Self::Io(error) => Some(error),
_ => None,
}
}
}
impl From<io::Error> for Error {
fn from(error: io::Error) -> Self {
Self::Io(error)
}
}