use std::any::type_name;
use std::borrow::Cow;
use std::error::Error as StdError;
use std::fmt::Display;
use std::io;
use std::result::Result as StdResult;
use crate::database::Database;
use crate::type_info::TypeInfo;
use crate::types::Type;
pub type Result<T> = StdResult<T, Error>;
pub type BoxDynError = Box<dyn StdError + 'static + Send + Sync>;
#[derive(thiserror::Error, Debug)]
#[error("unexpected null; try decoding as an `Option`")]
pub struct UnexpectedNullError;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("error with configuration: {0}")]
Configuration(#[source] BoxDynError),
#[error("error returned from database: {0}")]
Database(#[source] Box<dyn DatabaseError>),
#[error("error communicating with the server: {0}")]
Io(#[from] io::Error),
#[error("error occurred while attempting to establish a TLS connection: {0}")]
Tls(#[source] BoxDynError),
#[error("encountered unexpected or invalid data: {0}")]
Protocol(String),
#[error("no rows returned by a query that expected to return at least one row")]
RowNotFound,
#[error("type named {type_name} not found")]
TypeNotFound { type_name: String },
#[error("column index out of bounds: the len is {len}, but the index is {index}")]
ColumnIndexOutOfBounds { index: usize, len: usize },
#[error("no column found for name: {0}")]
ColumnNotFound(String),
#[error("error occurred while decoding column {index}: {source}")]
ColumnDecode {
index: String,
#[source]
source: BoxDynError,
},
#[error("error occurred while decoding: {0}")]
Decode(#[source] BoxDynError),
#[error("pool timed out while waiting for an open connection")]
PoolTimedOut,
#[error("attempted to acquire a connection on a closed pool")]
PoolClosed,
#[error("attempted to communicate with a crashed background worker")]
WorkerCrashed,
#[cfg(feature = "migrate")]
#[error("{0}")]
Migrate(#[source] Box<crate::migrate::MigrateError>),
}
impl StdError for Box<dyn DatabaseError> {}
impl Error {
pub fn into_database_error(self) -> Option<Box<dyn DatabaseError + 'static>> {
match self {
Error::Database(err) => Some(err),
_ => None,
}
}
pub fn as_database_error(&self) -> Option<&(dyn DatabaseError + 'static)> {
match self {
Error::Database(err) => Some(&**err),
_ => None,
}
}
#[allow(dead_code)]
#[inline]
pub(crate) fn protocol(err: impl Display) -> Self {
Error::Protocol(err.to_string())
}
#[allow(dead_code)]
#[inline]
pub(crate) fn config(err: impl StdError + Send + Sync + 'static) -> Self {
Error::Configuration(err.into())
}
}
pub(crate) fn mismatched_types<DB: Database, T: Type<DB>>(ty: &DB::TypeInfo) -> BoxDynError {
format!(
"mismatched types; Rust type `{}` (as SQL type `{}`) is not compatible with SQL type `{}`",
type_name::<T>(),
T::type_info().name(),
ty.name()
)
.into()
}
pub trait DatabaseError: 'static + Send + Sync + StdError {
fn message(&self) -> &str;
fn code(&self) -> Option<Cow<'_, str>> {
None
}
#[doc(hidden)]
fn as_error(&self) -> &(dyn StdError + Send + Sync + 'static);
#[doc(hidden)]
fn as_error_mut(&mut self) -> &mut (dyn StdError + Send + Sync + 'static);
#[doc(hidden)]
fn into_error(self: Box<Self>) -> Box<dyn StdError + Send + Sync + 'static>;
fn constraint(&self) -> Option<&str> {
None
}
}
impl dyn DatabaseError {
pub fn downcast_ref<E: DatabaseError>(&self) -> &E {
self.try_downcast_ref().unwrap_or_else(|| {
panic!(
"downcast to wrong DatabaseError type; original error: {}",
self
)
})
}
pub fn downcast<E: DatabaseError>(self: Box<Self>) -> Box<E> {
self.try_downcast().unwrap_or_else(|e| {
panic!(
"downcast to wrong DatabaseError type; original error: {}",
e
)
})
}
#[inline]
pub fn try_downcast_ref<E: DatabaseError>(&self) -> Option<&E> {
self.as_error().downcast_ref()
}
#[inline]
pub fn try_downcast<E: DatabaseError>(self: Box<Self>) -> StdResult<Box<E>, Box<Self>> {
if self.as_error().is::<E>() {
Ok(self.into_error().downcast().unwrap())
} else {
Err(self)
}
}
}
impl<E> From<E> for Error
where
E: DatabaseError,
{
#[inline]
fn from(error: E) -> Self {
Error::Database(Box::new(error))
}
}
#[cfg(feature = "migrate")]
impl From<crate::migrate::MigrateError> for Error {
#[inline]
fn from(error: crate::migrate::MigrateError) -> Self {
Error::Migrate(Box::new(error))
}
}
#[cfg(feature = "_tls-native-tls")]
impl From<sqlx_rt::native_tls::Error> for Error {
#[inline]
fn from(error: sqlx_rt::native_tls::Error) -> Self {
Error::Tls(Box::new(error))
}
}
#[cfg(feature = "_tls-rustls")]
impl From<webpki::InvalidDNSNameError> for Error {
#[inline]
fn from(error: webpki::InvalidDNSNameError) -> Self {
Error::Tls(Box::new(error))
}
}
macro_rules! err_protocol {
($expr:expr) => {
$crate::error::Error::Protocol($expr.into())
};
($fmt:expr, $($arg:tt)*) => {
$crate::error::Error::Protocol(format!($fmt, $($arg)*))
};
}