use std::error::Error as StdError;
use std::ffi::NulError;
use std::fmt::{self, Display};
#[derive(Debug)]
#[allow(clippy::enum_variant_names)]
#[non_exhaustive]
pub enum Error {
InvalidCString(NulError),
DatabaseError(
DatabaseErrorKind,
Box<dyn DatabaseErrorInformation + Send + Sync>,
),
NotFound,
QueryBuilderError(Box<dyn StdError + Send + Sync>),
DeserializationError(Box<dyn StdError + Send + Sync>),
SerializationError(Box<dyn StdError + Send + Sync>),
RollbackErrorOnCommit {
rollback_error: Box<Error>,
commit_error: Box<Error>,
},
RollbackTransaction,
AlreadyInTransaction,
NotInTransaction,
BrokenTransactionManager,
}
#[derive(Debug, PartialEq, Clone, Copy)]
#[non_exhaustive]
pub enum DatabaseErrorKind {
UniqueViolation = 0,
ForeignKeyViolation = 1,
UnableToSendCommand = 2,
SerializationFailure = 3,
ReadOnlyTransaction = 4,
RestrictViolation = 9,
NotNullViolation = 5,
CheckViolation = 6,
ExclusionViolation = 10,
ClosedConnection = 7,
#[doc(hidden)]
Unknown = 8, }
pub trait DatabaseErrorInformation {
fn message(&self) -> &str;
fn details(&self) -> Option<&str>;
fn hint(&self) -> Option<&str>;
fn table_name(&self) -> Option<&str>;
fn column_name(&self) -> Option<&str>;
fn constraint_name(&self) -> Option<&str>;
fn statement_position(&self) -> Option<i32>;
}
impl fmt::Debug for dyn DatabaseErrorInformation + Send + Sync {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.message(), f)
}
}
impl DatabaseErrorInformation for String {
fn message(&self) -> &str {
self
}
fn details(&self) -> Option<&str> {
None
}
fn hint(&self) -> Option<&str> {
None
}
fn table_name(&self) -> Option<&str> {
None
}
fn column_name(&self) -> Option<&str> {
None
}
fn constraint_name(&self) -> Option<&str> {
None
}
fn statement_position(&self) -> Option<i32> {
None
}
}
#[derive(Debug, PartialEq)]
#[non_exhaustive]
pub enum ConnectionError {
InvalidCString(NulError),
BadConnection(String),
InvalidConnectionUrl(String),
CouldntSetupConfiguration(Error),
}
pub type QueryResult<T> = Result<T, Error>;
pub type ConnectionResult<T> = Result<T, ConnectionError>;
pub trait OptionalExtension<T> {
fn optional(self) -> Result<Option<T>, Error>;
}
impl<T> OptionalExtension<T> for QueryResult<T> {
fn optional(self) -> Result<Option<T>, Error> {
match self {
Ok(value) => Ok(Some(value)),
Err(Error::NotFound) => Ok(None),
Err(e) => Err(e),
}
}
}
pub trait OptionalEmptyChangesetExtension<T> {
fn optional_empty_changeset(self) -> Result<Option<T>, Error>;
}
impl<T> OptionalEmptyChangesetExtension<T> for QueryResult<T> {
fn optional_empty_changeset(self) -> Result<Option<T>, Error> {
match self {
Ok(value) => Ok(Some(value)),
Err(Error::QueryBuilderError(e)) if e.is::<EmptyChangeset>() => Ok(None),
Err(e) => Err(e),
}
}
}
impl From<NulError> for ConnectionError {
fn from(e: NulError) -> Self {
ConnectionError::InvalidCString(e)
}
}
impl From<NulError> for Error {
fn from(e: NulError) -> Self {
Error::InvalidCString(e)
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Error::InvalidCString(ref nul_err) => write!(f, "{nul_err}"),
Error::DatabaseError(_, ref e) => write!(f, "{}", e.message()),
Error::NotFound => f.write_str("Record not found"),
Error::QueryBuilderError(ref e) => e.fmt(f),
Error::DeserializationError(ref e) => e.fmt(f),
Error::SerializationError(ref e) => e.fmt(f),
Error::RollbackErrorOnCommit {
ref rollback_error,
ref commit_error,
} => {
write!(
f,
"Transaction rollback failed: {} \
(rollback attempted because of failure to commit: {})",
&**rollback_error, &**commit_error
)?;
Ok(())
}
Error::RollbackTransaction => {
write!(f, "You have asked diesel to rollback the transaction")
}
Error::BrokenTransactionManager => write!(f, "The transaction manager is broken"),
Error::AlreadyInTransaction => write!(
f,
"Cannot perform this operation while a transaction is open",
),
Error::NotInTransaction => {
write!(f, "Cannot perform this operation outside of a transaction",)
}
}
}
}
impl StdError for Error {
fn cause(&self) -> Option<&dyn StdError> {
match *self {
Error::InvalidCString(ref e) => Some(e),
Error::QueryBuilderError(ref e) => Some(&**e),
Error::DeserializationError(ref e) => Some(&**e),
Error::SerializationError(ref e) => Some(&**e),
_ => None,
}
}
}
impl Display for ConnectionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ConnectionError::InvalidCString(ref nul_err) => nul_err.fmt(f),
ConnectionError::BadConnection(ref s) => write!(f, "{s}"),
ConnectionError::InvalidConnectionUrl(ref s) => write!(f, "{s}"),
ConnectionError::CouldntSetupConfiguration(ref e) => e.fmt(f),
}
}
}
impl StdError for ConnectionError {
fn cause(&self) -> Option<&dyn StdError> {
match *self {
ConnectionError::InvalidCString(ref e) => Some(e),
ConnectionError::CouldntSetupConfiguration(ref e) => Some(e),
_ => None,
}
}
}
impl PartialEq for Error {
fn eq(&self, other: &Error) -> bool {
match (self, other) {
(Error::InvalidCString(a), Error::InvalidCString(b)) => a == b,
(Error::DatabaseError(_, a), Error::DatabaseError(_, b)) => a.message() == b.message(),
(&Error::NotFound, &Error::NotFound) => true,
(&Error::RollbackTransaction, &Error::RollbackTransaction) => true,
(&Error::AlreadyInTransaction, &Error::AlreadyInTransaction) => true,
_ => false,
}
}
}
#[cfg(test)]
#[allow(warnings)]
fn error_impls_send() {
let err: Error = unimplemented!();
let x: &dyn Send = &err;
}
#[derive(Debug, Clone, Copy)]
pub struct UnexpectedNullError;
impl fmt::Display for UnexpectedNullError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Unexpected null for non-null column")
}
}
impl StdError for UnexpectedNullError {}
#[derive(Debug, Clone, Copy)]
pub struct UnexpectedEndOfRow;
impl fmt::Display for UnexpectedEndOfRow {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Unexpected end of row")
}
}
impl StdError for UnexpectedEndOfRow {}
#[derive(Debug, Clone, Copy)]
pub struct EmptyChangeset;
impl fmt::Display for EmptyChangeset {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"There are no changes to save. This query cannot be built"
)
}
}
impl StdError for EmptyChangeset {}
#[derive(Debug, Clone, Copy)]
pub struct EmptyQuery;
impl fmt::Display for EmptyQuery {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Detected an empty query. These are not supported by your database system"
)
}
}
impl StdError for EmptyQuery {}
#[derive(Debug)]
#[non_exhaustive]
pub struct DeserializeFieldError {
pub field_name: Option<String>,
pub error: Box<dyn StdError + Send + Sync>,
}
impl DeserializeFieldError {
#[cold]
pub(crate) fn new<'a, F, DB>(field: F, error: Box<dyn std::error::Error + Send + Sync>) -> Self
where
DB: crate::backend::Backend,
F: crate::row::Field<'a, DB>,
{
DeserializeFieldError {
field_name: field.field_name().map(|s| s.to_string()),
error,
}
}
}
impl StdError for DeserializeFieldError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
Some(&*self.error)
}
}
impl fmt::Display for DeserializeFieldError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(ref field_name) = self.field_name {
write!(
f,
"Error deserializing field '{}': {}",
field_name, self.error
)
} else {
write!(f, "Error deserializing field: {}", self.error)
}
}
}