use std::fmt;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum DatabaseErrorKind {
ConnectionFailed,
ConnectionLost,
QuerySyntax,
QueryExecution,
ConstraintViolation,
Deadlock,
SerializationFailure,
TransactionTimeout,
NestedTransaction,
NoRows,
TooManyRows,
TypeMismatch,
SchemaMismatch,
DatabaseLocked,
DiskFull,
PermissionDenied,
ReadOnly,
}
impl DatabaseErrorKind {
#[inline]
pub fn is_retryable(&self) -> bool {
match self {
DatabaseErrorKind::ConnectionFailed => true,
DatabaseErrorKind::ConnectionLost => true,
DatabaseErrorKind::QuerySyntax => false,
DatabaseErrorKind::QueryExecution => false,
DatabaseErrorKind::ConstraintViolation => false,
DatabaseErrorKind::Deadlock => true,
DatabaseErrorKind::SerializationFailure => true,
DatabaseErrorKind::TransactionTimeout => true,
DatabaseErrorKind::NestedTransaction => false,
DatabaseErrorKind::NoRows => false,
DatabaseErrorKind::TooManyRows => false,
DatabaseErrorKind::TypeMismatch => false,
DatabaseErrorKind::SchemaMismatch => false,
DatabaseErrorKind::DatabaseLocked => true,
DatabaseErrorKind::DiskFull => false,
DatabaseErrorKind::PermissionDenied => false,
DatabaseErrorKind::ReadOnly => false,
}
}
#[inline]
pub fn is_connection_error(&self) -> bool {
matches!(
self,
DatabaseErrorKind::ConnectionFailed | DatabaseErrorKind::ConnectionLost
)
}
#[inline]
pub fn is_query_error(&self) -> bool {
matches!(
self,
DatabaseErrorKind::QuerySyntax
| DatabaseErrorKind::QueryExecution
| DatabaseErrorKind::TypeMismatch
)
}
#[inline]
pub fn is_transaction_error(&self) -> bool {
matches!(
self,
DatabaseErrorKind::Deadlock
| DatabaseErrorKind::SerializationFailure
| DatabaseErrorKind::TransactionTimeout
| DatabaseErrorKind::NestedTransaction
)
}
#[inline]
pub fn is_data_error(&self) -> bool {
matches!(
self,
DatabaseErrorKind::ConstraintViolation
| DatabaseErrorKind::NoRows
| DatabaseErrorKind::TooManyRows
)
}
#[inline]
pub fn is_configuration_error(&self) -> bool {
matches!(
self,
DatabaseErrorKind::SchemaMismatch
| DatabaseErrorKind::ReadOnly
| DatabaseErrorKind::PermissionDenied
)
}
#[inline]
pub fn category(&self) -> &str {
if self.is_connection_error() {
"Connection"
} else if self.is_query_error() {
"Query"
} else if self.is_transaction_error() {
"Transaction"
} else if self.is_data_error() {
"Data"
} else if self.is_configuration_error() {
"Configuration"
} else {
"System"
}
}
#[inline]
pub fn to_machine_string(&self) -> &'static str {
match self {
DatabaseErrorKind::ConnectionFailed => "connection_failed",
DatabaseErrorKind::ConnectionLost => "connection_lost",
DatabaseErrorKind::QuerySyntax => "query_syntax",
DatabaseErrorKind::QueryExecution => "query_execution",
DatabaseErrorKind::ConstraintViolation => "constraint_violation",
DatabaseErrorKind::Deadlock => "deadlock",
DatabaseErrorKind::SerializationFailure => "serialization_failure",
DatabaseErrorKind::TransactionTimeout => "transaction_timeout",
DatabaseErrorKind::NestedTransaction => "nested_transaction",
DatabaseErrorKind::NoRows => "no_rows",
DatabaseErrorKind::TooManyRows => "too_many_rows",
DatabaseErrorKind::TypeMismatch => "type_mismatch",
DatabaseErrorKind::SchemaMismatch => "schema_mismatch",
DatabaseErrorKind::DatabaseLocked => "database_locked",
DatabaseErrorKind::DiskFull => "disk_full",
DatabaseErrorKind::PermissionDenied => "permission_denied",
DatabaseErrorKind::ReadOnly => "read_only",
}
}
}
impl fmt::Display for DatabaseErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DatabaseErrorKind::ConnectionFailed => write!(f, "connection failed"),
DatabaseErrorKind::ConnectionLost => write!(f, "connection lost"),
DatabaseErrorKind::QuerySyntax => write!(f, "query syntax error"),
DatabaseErrorKind::QueryExecution => write!(f, "query execution error"),
DatabaseErrorKind::ConstraintViolation => write!(f, "constraint violation"),
DatabaseErrorKind::Deadlock => write!(f, "deadlock detected"),
DatabaseErrorKind::SerializationFailure => write!(f, "serialization failure"),
DatabaseErrorKind::TransactionTimeout => write!(f, "transaction timeout"),
DatabaseErrorKind::NestedTransaction => write!(f, "nested transaction"),
DatabaseErrorKind::NoRows => write!(f, "no rows returned"),
DatabaseErrorKind::TooManyRows => write!(f, "too many rows returned"),
DatabaseErrorKind::TypeMismatch => write!(f, "type mismatch"),
DatabaseErrorKind::SchemaMismatch => write!(f, "schema mismatch"),
DatabaseErrorKind::DatabaseLocked => write!(f, "database locked"),
DatabaseErrorKind::DiskFull => write!(f, "disk full"),
DatabaseErrorKind::PermissionDenied => write!(f, "permission denied"),
DatabaseErrorKind::ReadOnly => write!(f, "database is read-only"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_connection_failed_retryable() {
assert!(DatabaseErrorKind::ConnectionFailed.is_retryable());
}
#[test]
fn test_query_syntax_not_retryable() {
assert!(!DatabaseErrorKind::QuerySyntax.is_retryable());
}
#[test]
fn test_constraint_violation_not_retryable() {
assert!(!DatabaseErrorKind::ConstraintViolation.is_retryable());
}
#[test]
fn test_deadlock_retryable() {
assert!(DatabaseErrorKind::Deadlock.is_retryable());
}
#[test]
fn test_display() {
assert_eq!(
DatabaseErrorKind::ConnectionFailed.to_string(),
"connection failed"
);
assert_eq!(
DatabaseErrorKind::ConstraintViolation.to_string(),
"constraint violation"
);
assert_eq!(DatabaseErrorKind::Deadlock.to_string(), "deadlock detected");
}
}