use thiserror::Error;
pub type OrmResult<T> = Result<T, OrmError>;
#[derive(Debug, Error)]
pub enum OrmError {
#[error("Connection error: {0}")]
Connection(String),
#[error("Query error: {0}")]
Query(#[from] tokio_postgres::Error),
#[error("Not found: {0}")]
NotFound(String),
#[error("Too many rows: expected {expected}, got {got}")]
TooManyRows { expected: usize, got: usize },
#[error("Unique constraint violation: {0}")]
UniqueViolation(String),
#[error("Foreign key violation: {0}")]
ForeignKeyViolation(String),
#[error("Check constraint violation: {0}")]
CheckViolation(String),
#[error("Decode error on column '{column}': {message}")]
Decode { column: String, message: String },
#[error("Validation error: {0}")]
Validation(String),
#[error("Serialization error: {0}")]
Serialization(String),
#[cfg(feature = "pool")]
#[error("Pool error: {0}")]
Pool(String),
#[error("Query timeout after {0:?}")]
Timeout(std::time::Duration),
#[cfg(feature = "migrate")]
#[error("Migration error: {0}")]
Migration(String),
#[error("{0}")]
Other(String),
}
impl OrmError {
pub fn decode(column: impl Into<String>, message: impl Into<String>) -> Self {
Self::Decode {
column: column.into(),
message: message.into(),
}
}
pub fn not_found(message: impl Into<String>) -> Self {
Self::NotFound(message.into())
}
pub fn too_many_rows(expected: usize, got: usize) -> Self {
Self::TooManyRows { expected, got }
}
pub fn validation(message: impl Into<String>) -> Self {
Self::Validation(message.into())
}
pub fn is_unique_violation(&self) -> bool {
matches!(self, Self::UniqueViolation(_))
}
pub fn is_not_found(&self) -> bool {
matches!(self, Self::NotFound(_))
}
pub fn is_too_many_rows(&self) -> bool {
matches!(self, Self::TooManyRows { .. })
}
pub fn is_timeout(&self) -> bool {
matches!(self, Self::Timeout(_))
}
pub fn from_db_error(err: tokio_postgres::Error) -> Self {
if let Some(db_err) = err.as_db_error() {
let constraint = db_err.constraint().unwrap_or("unknown");
let message = db_err.message();
match db_err.code().code() {
"23505" => return Self::UniqueViolation(format!("{constraint}: {message}")),
"23503" => {
return Self::ForeignKeyViolation(format!("{constraint}: {message}"));
}
"23514" => return Self::CheckViolation(format!("{constraint}: {message}")),
_ => {}
}
}
Self::Query(err)
}
}
#[cfg(feature = "pool")]
impl From<deadpool_postgres::PoolError> for OrmError {
fn from(err: deadpool_postgres::PoolError) -> Self {
Self::Pool(err.to_string())
}
}
#[cfg(feature = "migrate")]
impl From<refinery::Error> for OrmError {
fn from(err: refinery::Error) -> Self {
Self::Migration(err.to_string())
}
}