use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StorageOp {
Open,
Read,
Write,
Delete,
Search,
Migrate,
Snapshot,
}
impl fmt::Display for StorageOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StorageOp::Open => write!(f, "open"),
StorageOp::Read => write!(f, "read"),
StorageOp::Write => write!(f, "write"),
StorageOp::Delete => write!(f, "delete"),
StorageOp::Search => write!(f, "search"),
StorageOp::Migrate => write!(f, "migrate"),
StorageOp::Snapshot => write!(f, "snapshot"),
}
}
}
#[derive(Debug)]
pub enum StorageError {
Io {
op: StorageOp,
detail: String,
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
Sqlite {
op: StorageOp,
detail: String,
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
NotFound {
op: StorageOp,
detail: String,
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
AlreadyExists {
op: StorageOp,
detail: String,
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
Migration {
op: StorageOp,
detail: String,
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
InvalidData {
op: StorageOp,
detail: String,
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
Corruption {
op: StorageOp,
detail: String,
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
DatabaseLocked {
op: StorageOp,
detail: String,
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
ForeignKeyViolation {
op: StorageOp,
detail: String,
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
SchemaMismatch {
expected: String,
found: String,
},
}
impl StorageError {
pub fn op(&self) -> StorageOp {
match self {
StorageError::Io { op, .. }
| StorageError::Sqlite { op, .. }
| StorageError::NotFound { op, .. }
| StorageError::AlreadyExists { op, .. }
| StorageError::Migration { op, .. }
| StorageError::InvalidData { op, .. }
| StorageError::Corruption { op, .. }
| StorageError::DatabaseLocked { op, .. }
| StorageError::ForeignKeyViolation { op, .. } => *op,
StorageError::SchemaMismatch { .. } => StorageOp::Migrate,
}
}
pub fn detail(&self) -> &str {
match self {
StorageError::Io { detail, .. }
| StorageError::Sqlite { detail, .. }
| StorageError::NotFound { detail, .. }
| StorageError::AlreadyExists { detail, .. }
| StorageError::Migration { detail, .. }
| StorageError::InvalidData { detail, .. }
| StorageError::Corruption { detail, .. }
| StorageError::DatabaseLocked { detail, .. }
| StorageError::ForeignKeyViolation { detail, .. } => detail,
StorageError::SchemaMismatch { expected, .. } => expected,
}
}
}
impl fmt::Display for StorageError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StorageError::Io { op, detail, .. } => {
write!(f, "storage I/O error during {op}: {detail}")
}
StorageError::Sqlite { op, detail, .. } => {
write!(f, "SQLite error during {op}: {detail}")
}
StorageError::NotFound { op, detail, .. } => {
write!(f, "not found during {op}: {detail}")
}
StorageError::AlreadyExists { op, detail, .. } => {
write!(f, "already exists during {op}: {detail}")
}
StorageError::Migration { op, detail, .. } => {
write!(f, "migration error during {op}: {detail}")
}
StorageError::InvalidData { op, detail, .. } => {
write!(f, "invalid data during {op}: {detail}")
}
StorageError::Corruption { op, detail, .. } => {
write!(f, "corruption detected during {op}: {detail}")
}
StorageError::DatabaseLocked { op, detail, .. } => {
write!(f, "database is locked during {op}: {detail}")
}
StorageError::ForeignKeyViolation { op, detail, .. } => {
write!(f, "foreign key violation during {op}: {detail}")
}
StorageError::SchemaMismatch { expected, found } => {
write!(f, "schema version mismatch: expected {expected}, found {found}")
}
}
}
}
impl std::error::Error for StorageError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
let src = match self {
StorageError::Io { source, .. }
| StorageError::Sqlite { source, .. }
| StorageError::NotFound { source, .. }
| StorageError::AlreadyExists { source, .. }
| StorageError::Migration { source, .. }
| StorageError::InvalidData { source, .. }
| StorageError::Corruption { source, .. }
| StorageError::DatabaseLocked { source, .. }
| StorageError::ForeignKeyViolation { source, .. } => source,
StorageError::SchemaMismatch { .. } => return None,
};
src.as_ref().map(|e| e.as_ref() as &(dyn std::error::Error + 'static))
}
}
impl From<std::io::Error> for StorageError {
fn from(err: std::io::Error) -> Self {
StorageError::Io {
op: StorageOp::Read,
detail: err.to_string(),
source: Some(Box::new(err)),
}
}
}
impl From<serde_json::Error> for StorageError {
fn from(err: serde_json::Error) -> Self {
StorageError::InvalidData {
op: StorageOp::Read,
detail: err.to_string(),
source: Some(Box::new(err)),
}
}
}
pub type StorageResult<T> = Result<T, StorageError>;