use thiserror::Error;
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DatabaseErrorKind {
Connection,
ConnectionHandleExpired,
Timeout,
UniqueViolation,
ForeignKeyViolation,
NotNullViolation,
CheckViolation,
Syntax,
Type,
ColumnNotFound,
Transaction,
Configuration,
Serialization,
Unsupported,
Query,
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[error("{message}")]
pub struct DatabaseError {
kind: DatabaseErrorKind,
message: String,
code: Option<String>,
}
impl DatabaseError {
pub fn new(kind: DatabaseErrorKind, message: impl Into<String>) -> Self {
Self {
kind,
message: message.into(),
code: None,
}
}
pub fn with_code(mut self, code: impl Into<String>) -> Self {
self.code = Some(code.into());
self
}
pub fn kind(&self) -> DatabaseErrorKind {
self.kind
}
pub fn message(&self) -> &str {
&self.message
}
pub fn code(&self) -> Option<&str> {
self.code.as_deref()
}
}
#[cfg(test)]
mod tests {
use super::{DatabaseError, DatabaseErrorKind};
#[test]
fn connection_handle_expired_has_stable_display_and_server_status() {
let error = DatabaseError::new(
DatabaseErrorKind::ConnectionHandleExpired,
"The injected database connection is no longer available because its DI scope has ended",
);
assert_eq!(
error.to_string(),
"The injected database connection is no longer available because its DI scope has ended"
);
assert_eq!(crate::exception::Error::from(error).status_code(), 500);
}
}