use std::borrow::Cow;
use std::io;
use std::result;
use std::str::Utf8Error;
use std::string::FromUtf8Error;
use thiserror::Error;
use tokio::time::error::Elapsed;
use url::ParseError;
pub type Result<T> = result::Result<T, Error>;
#[derive(Debug, Error)]
pub enum Error {
#[error("Driver error: `{}`", _0)]
Driver(#[source] DriverError),
#[error("Input/output error: `{}`", _0)]
IO(#[source] io::Error),
#[error("Connections error: `{}`", _0)]
Connection(#[source] ConnectionError),
#[error("Other error: `{}`", _0)]
Other(Cow<'static, str>),
#[error("Server error: `{}`", _0)]
Server(#[source] ServerError),
#[error("URL error: `{}`", _0)]
Url(#[source] UrlError),
#[error("From SQL error: `{}`", _0)]
FromSql(#[source] FromSqlError),
}
#[derive(Debug, Error, Clone)]
#[error("ERROR {} ({:?}): {}", name, code, message)]
pub struct ServerError {
pub code: u32,
pub name: String,
pub message: String,
pub stack_trace: String,
}
#[derive(Debug, Error)]
pub enum ConnectionError {
#[error("TLS connection requires hostname to be provided")]
TlsHostNotProvided,
#[error("Input/output error: `{}`", _0)]
IOError(#[source] io::Error),
#[cfg(feature = "tls")]
#[error("TLS connection error: `{}`", _0)]
TlsError(#[source] tokio_native_tls::native_tls::Error),
}
#[derive(Debug, Error, Clone)]
pub enum UrlError {
#[error("Invalid or incomplete connection URL")]
Invalid,
#[error("Invalid value `{}' for connection URL parameter `{}'", value, param)]
InvalidParamValue { param: String, value: String },
#[error("URL parse error: {}", _0)]
Parse(#[source] ParseError),
#[error("Unknown connection URL parameter `{}'", param)]
UnknownParameter { param: String },
#[error("Unsupported connection URL scheme `{}'", scheme)]
UnsupportedScheme { scheme: String },
}
#[derive(Debug, Error, Clone)]
pub enum DriverError {
#[error("Varint overflows a 64-bit integer.")]
Overflow,
#[error("Unknown packet 0x{:x}.", packet)]
UnknownPacket { packet: u64 },
#[error("Unexpected packet.")]
UnexpectedPacket,
#[error("Timeout error.")]
Timeout,
#[error("Invalid utf-8 sequence.")]
Utf8Error(Utf8Error),
#[error("UnknownSetting name {}", name)]
UnknownSetting { name: String },
}
#[derive(Debug, Error, Clone)]
pub enum FromSqlError {
#[error("SqlType::{} cannot be cast to {}.", src, dst)]
InvalidType {
src: Cow<'static, str>,
dst: Cow<'static, str>,
},
#[error("Out of range.")]
OutOfRange,
#[error("Unsupported operation.")]
UnsupportedOperation,
}
impl Error {
pub(crate) fn is_would_block(&self) -> bool {
if let Error::IO(ref e) = self {
if e.kind() == io::ErrorKind::WouldBlock {
return true;
}
}
false
}
}
impl From<ConnectionError> for Error {
fn from(error: ConnectionError) -> Self {
Error::Connection(error)
}
}
#[cfg(feature = "tls")]
impl From<tokio_native_tls::native_tls::Error> for ConnectionError {
fn from(error: tokio_native_tls::native_tls::Error) -> Self {
ConnectionError::TlsError(error)
}
}
impl From<DriverError> for Error {
fn from(err: DriverError) -> Self {
Error::Driver(err)
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Error::IO(err)
}
}
impl From<Error> for io::Error {
fn from(err: Error) -> Self {
match err {
Error::IO(error) => error,
e => io::Error::new(io::ErrorKind::Other, e.to_string()),
}
}
}
impl From<ServerError> for Error {
fn from(err: ServerError) -> Self {
Error::Server(err)
}
}
impl From<UrlError> for Error {
fn from(err: UrlError) -> Self {
Error::Url(err)
}
}
impl From<String> for Error {
fn from(err: String) -> Self {
Error::Other(Cow::from(err))
}
}
impl From<&str> for Error {
fn from(err: &str) -> Self {
Error::Other(err.to_string().into())
}
}
impl From<FromUtf8Error> for Error {
fn from(err: FromUtf8Error) -> Self {
Error::Other(err.to_string().into())
}
}
impl From<Elapsed> for Error {
fn from(_err: Elapsed) -> Self {
Error::Driver(DriverError::Timeout)
}
}
impl From<ParseError> for Error {
fn from(err: ParseError) -> Self {
Error::Url(UrlError::Parse(err))
}
}
impl From<Utf8Error> for Error {
fn from(err: Utf8Error) -> Self {
Error::Driver(DriverError::Utf8Error(err))
}
}
impl Error {
pub fn exception_name(&self) -> &str {
match self {
Error::Driver(_) => "DriverException",
Error::IO(_) => "IOException",
Error::Connection(_) => "ConnectionException",
Error::Other(_) => "OtherException",
Error::Server(e) => e.name.as_str(),
Error::Url(_) => "URLException",
Error::FromSql(_) => "SQLException",
}
}
}