use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Error, Debug)]
pub enum Error {
#[error("WAL error: {message}")]
WriteAheadLog {
message: String,
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
#[error("SSTable error: {message}")]
SSTable {
message: String,
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
#[error("MemTable error: {message}")]
MemTable { message: String },
#[error("Compaction failed: {reason}")]
Compaction { reason: String },
#[error("Index corruption detected: {details}")]
IndexCorruption { details: String },
#[error("Query error: {message}")]
QueryError { message: String },
#[error("Merkle chain validation failed: expected {expected}, got {actual}")]
MerkleValidation { expected: String, actual: String },
#[error("Tampering detected at position {position}")]
TamperingDetected { position: u64 },
#[error("IO error: {message}")]
Io {
message: String,
source: std::io::Error,
},
#[error("Configuration error: {message}")]
Configuration { message: String },
#[error("Resource exhausted: {resource}")]
ResourceExhausted { resource: String },
#[error("Internal error: {message}")]
Internal { message: String },
}
impl Error {
pub fn is_recoverable(&self) -> bool {
matches!(self, Error::ResourceExhausted { .. })
}
pub fn error_code(&self) -> &'static str {
match self {
Error::WriteAheadLog { .. } => "WAL_ERROR",
Error::SSTable { .. } => "SSTABLE_ERROR",
Error::MemTable { .. } => "MEMTABLE_ERROR",
Error::Compaction { .. } => "COMPACTION_ERROR",
Error::IndexCorruption { .. } => "INDEX_CORRUPTION",
Error::QueryError { .. } => "QUERY_ERROR",
Error::MerkleValidation { .. } => "MERKLE_VALIDATION_FAILED",
Error::TamperingDetected { .. } => "TAMPERING_DETECTED",
Error::Io { .. } => "IO_ERROR",
Error::Configuration { .. } => "CONFIG_ERROR",
Error::ResourceExhausted { .. } => "RESOURCE_EXHAUSTED",
Error::Internal { .. } => "INTERNAL_ERROR",
}
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Error::Io {
message: err.to_string(),
source: err,
}
}
}
pub trait ResultExt<T> {
fn with_context<F>(self, f: F) -> Result<T>
where
F: FnOnce() -> String;
}
impl<T> ResultExt<T> for Result<T> {
fn with_context<F>(self, f: F) -> Result<T>
where
F: FnOnce() -> String,
{
self.map_err(|e| Error::Internal {
message: format!("{}: {}", f(), e),
})
}
}