use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorKind {
Connection,
Query,
Conversion,
NotFound,
MultipleFound,
Configuration,
Conflict,
}
impl ErrorKind {
pub fn as_str(self) -> &'static str {
match self {
ErrorKind::Connection => "connection",
ErrorKind::Query => "query",
ErrorKind::Conversion => "conversion",
ErrorKind::NotFound => "not_found",
ErrorKind::MultipleFound => "multiple_found",
ErrorKind::Configuration => "configuration",
ErrorKind::Conflict => "conflict",
}
}
}
#[derive(Debug)]
pub struct OrmError {
kind: ErrorKind,
message: String,
source: Option<Box<dyn std::error::Error + Send + Sync>>,
}
impl OrmError {
pub fn new(kind: ErrorKind, message: impl Into<String>) -> Self {
Self {
kind,
message: message.into(),
source: None,
}
}
pub fn with_source(
mut self,
source: impl std::error::Error + Send + Sync + 'static,
) -> Self {
self.source = Some(Box::new(source));
self
}
pub fn kind(&self) -> ErrorKind {
self.kind
}
pub fn message(&self) -> &str {
&self.message
}
pub fn is_retryable(&self) -> bool {
use std::error::Error;
let mut text = self.message.to_lowercase();
let mut source = self.source();
while let Some(error) = source {
text.push(' ');
text.push_str(&error.to_string().to_lowercase());
source = error.source();
}
const MARKERS: [&str; 8] = [
"database is locked",
"deadlock",
"serialization",
"could not serialize",
"lock wait timeout",
"sqlite_busy",
"40001", "40p01", ];
MARKERS.iter().any(|marker| text.contains(marker))
}
pub fn connection(message: impl Into<String>) -> Self {
Self::new(ErrorKind::Connection, message)
}
pub fn query(message: impl Into<String>) -> Self {
Self::new(ErrorKind::Query, message)
}
pub fn conversion(message: impl Into<String>) -> Self {
Self::new(ErrorKind::Conversion, message)
}
pub fn not_found(message: impl Into<String>) -> Self {
Self::new(ErrorKind::NotFound, message)
}
pub fn multiple_found(message: impl Into<String>) -> Self {
Self::new(ErrorKind::MultipleFound, message)
}
pub fn conflict(message: impl Into<String>) -> Self {
Self::new(ErrorKind::Conflict, message)
}
pub fn configuration(message: impl Into<String>) -> Self {
Self::new(ErrorKind::Configuration, message)
}
}
impl fmt::Display for OrmError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.kind.as_str(), self.message)
}
}
impl std::error::Error for OrmError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.source
.as_ref()
.map(|boxed| boxed.as_ref() as &(dyn std::error::Error + 'static))
}
}
pub type Result<T, E = OrmError> = std::result::Result<T, E>;