use std::fmt;
use crate::types::{CanonicalEdgeKey, VertexKey};
#[derive(Debug)]
pub enum StoreError {
RocksDb(rocksdb::Error),
Io(std::io::Error),
CorruptData(&'static str),
MissingColumnFamily(&'static str),
Conflict,
LockError,
SchemaViolation(String),
SchemaConflict(String),
SchemaExhausted(String),
NotFound,
DuplicateVertex(VertexKey),
DuplicateEdge(CanonicalEdgeKey),
Tombstoned,
IncidentEdges,
ReadOnly,
UnexpectedDataType(String),
UnsupportedOperation(String),
TraversalError(String),
IncompleteLoad { msg: String },
}
impl StoreError {
pub fn is_retryable(&self) -> bool {
matches!(self, Self::Conflict | Self::LockError)
}
pub fn is_storage_failure(&self) -> bool {
matches!(
self,
Self::RocksDb(_)
| Self::Io(_)
| Self::CorruptData(_)
| Self::MissingColumnFamily(_)
| Self::IncompleteLoad { .. }
)
}
pub fn is_schema_error(&self) -> bool {
matches!(self, Self::SchemaViolation(_) | Self::SchemaConflict(_) | Self::SchemaExhausted(_))
}
pub fn is_query_error(&self) -> bool {
matches!(self, Self::TraversalError(_) | Self::UnsupportedOperation(_) | Self::UnexpectedDataType(_))
}
pub fn category(&self) -> &'static str {
match self {
Self::RocksDb(_) | Self::Io(_) | Self::CorruptData(_) | Self::MissingColumnFamily(_) => "storage",
Self::Conflict | Self::LockError => "transaction",
Self::SchemaViolation(_) | Self::SchemaConflict(_) | Self::SchemaExhausted(_) => "schema",
Self::NotFound
| Self::DuplicateVertex(_)
| Self::DuplicateEdge(_)
| Self::Tombstoned
| Self::IncidentEdges
| Self::ReadOnly => "integrity",
Self::IncompleteLoad { .. } => "storage",
Self::UnexpectedDataType(_) | Self::UnsupportedOperation(_) | Self::TraversalError(_) => "query",
}
}
}
impl fmt::Display for StoreError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StoreError::RocksDb(e) => write!(f, "storage engine error: {e}"),
StoreError::Io(e) => write!(f, "I/O error: {e}"),
StoreError::CorruptData(ctx) => write!(f, "corrupt data: {ctx}"),
StoreError::MissingColumnFamily(name) => write!(f, "missing column family: {name}"),
StoreError::Conflict => write!(f, "transaction conflict; retry"),
StoreError::LockError => write!(f, "lock error"),
StoreError::SchemaViolation(msg) => write!(f, "schema violation: {msg}"),
StoreError::SchemaConflict(msg) => write!(f, "schema conflict: {msg}"),
StoreError::SchemaExhausted(msg) => write!(f, "schema exhausted: {msg}"),
StoreError::NotFound => write!(f, "key not found"),
StoreError::DuplicateVertex(key) => write!(f, "duplicate vertex: {key}"),
StoreError::DuplicateEdge(key) => write!(f, "duplicate edge: {key}"),
StoreError::Tombstoned => write!(f, "element is tombstoned"),
StoreError::IncidentEdges => write!(f, "cannot drop vertex with incident edges"),
StoreError::ReadOnly => write!(f, "write operation on read-only snapshot"),
StoreError::UnexpectedDataType(msg) => write!(f, "unexpected datatype: {msg}"),
StoreError::UnsupportedOperation(msg) => write!(f, "unsupported operation: {msg}"),
StoreError::TraversalError(msg) => write!(f, "traversal error: {msg}"),
StoreError::IncompleteLoad { msg } => write!(f, "incomplete bulk load: {}", msg),
}
}
}
impl std::error::Error for StoreError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
StoreError::RocksDb(e) => Some(e),
StoreError::Io(e) => Some(e),
_ => None,
}
}
}
impl From<rocksdb::Error> for StoreError {
fn from(e: rocksdb::Error) -> Self {
StoreError::RocksDb(e)
}
}
impl From<std::io::Error> for StoreError {
fn from(e: std::io::Error) -> Self {
StoreError::Io(e)
}
}