use std::convert::From;
use std::error::Error as StdError;
use std::ffi::NulError;
use std::fmt::{self, Display};
#[derive(Debug)]
#[allow(clippy::enum_variant_names)]
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>),
RollbackTransaction,
AlreadyInTransaction,
#[doc(hidden)]
__Nonexhaustive,
}
#[derive(Debug, Clone, Copy)]
pub enum DatabaseErrorKind {
UniqueViolation,
ForeignKeyViolation,
UnableToSendCommand,
SerializationFailure,
#[doc(hidden)]
__Unknown, }
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>;
}
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
}
}
#[derive(Debug, PartialEq)]
pub enum ConnectionError {
InvalidCString(NulError),
BadConnection(String),
InvalidConnectionUrl(String),
CouldntSetupConfiguration(Error),
#[doc(hidden)]
__Nonexhaustive, }
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),
}
}
}
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) => nul_err.fmt(f),
Error::DatabaseError(_, ref e) => write!(f, "{}", e.message()),
Error::NotFound => f.write_str("NotFound"),
Error::QueryBuilderError(ref e) => e.fmt(f),
Error::DeserializationError(ref e) => e.fmt(f),
Error::SerializationError(ref e) => e.fmt(f),
Error::RollbackTransaction => write!(f, "{}", self.description()),
Error::AlreadyInTransaction => write!(f, "{}", self.description()),
Error::__Nonexhaustive => unreachable!(),
}
}
}
impl StdError for Error {
fn description(&self) -> &str {
match *self {
Error::InvalidCString(ref nul_err) => nul_err.description(),
Error::DatabaseError(_, ref e) => e.message(),
Error::NotFound => "Record not found",
Error::QueryBuilderError(ref e) => e.description(),
Error::DeserializationError(ref e) => e.description(),
Error::SerializationError(ref e) => e.description(),
Error::RollbackTransaction => "The current transaction was aborted",
Error::AlreadyInTransaction => {
"Cannot perform this operation while a transaction is open"
}
Error::__Nonexhaustive => unreachable!(),
}
}
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),
ConnectionError::__Nonexhaustive => unreachable!(),
}
}
}
impl StdError for ConnectionError {
fn description(&self) -> &str {
match *self {
ConnectionError::InvalidCString(ref nul_err) => nul_err.description(),
ConnectionError::BadConnection(ref s) => s,
ConnectionError::InvalidConnectionUrl(ref s) => s,
ConnectionError::CouldntSetupConfiguration(ref e) => e.description(),
ConnectionError::__Nonexhaustive => unreachable!(),
}
}
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(ref a), &Error::InvalidCString(ref b)) => a == b,
(&Error::DatabaseError(_, ref a), &Error::DatabaseError(_, ref 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: &Send = &err;
}
pub(crate) fn first_or_not_found<T>(records: QueryResult<Vec<T>>) -> QueryResult<T> {
records?.into_iter().next().ok_or(Error::NotFound)
}
#[derive(Debug, Clone, Copy)]
pub struct UnexpectedNullError;
impl fmt::Display for UnexpectedNullError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())
}
}
impl StdError for UnexpectedNullError {
fn description(&self) -> &str {
"Unexpected null for non-null column"
}
}