use std::fmt;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("protocol error: {0}")]
Protocol(String),
#[error("{0}")]
Server(Box<ServerError>),
#[error("authentication failed: {0}")]
Auth(String),
#[error("tls error: {0}")]
Tls(String),
#[error("pool error: {0}")]
Pool(String),
#[error("config error: {0}")]
Config(String),
#[error("encode error: {0}")]
Encode(String),
#[error("decode error: {0}")]
Decode(String),
#[error("column not found: {0}")]
ColumnNotFound(String),
#[error("column index {index} out of bounds (row has {count} columns)")]
ColumnIndex { index: usize, count: usize },
#[error("unexpected null in column {0}")]
UnexpectedNull(usize),
#[error("timeout: {0}")]
Timeout(String),
#[error("connection closed")]
ConnectionClosed,
#[error("copy error: {0}")]
Copy(String),
#[error("transaction already completed")]
TransactionCompleted,
#[error("all hosts failed: {0}")]
AllHostsFailed(String),
#[error("wrong session attributes: {0}")]
WrongSessionAttrs(String),
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct ServerError {
pub severity: String,
pub code: String,
pub message: String,
pub detail: Option<String>,
pub hint: Option<String>,
pub position: Option<u32>,
}
impl fmt::Display for ServerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}: {} (SQLSTATE {})",
self.severity, self.message, self.code
)
}
}
impl Error {
pub fn code(&self) -> Option<&str> {
match self {
Error::Server(e) => Some(&e.code),
_ => None,
}
}
pub fn server_error(&self) -> Option<&ServerError> {
match self {
Error::Server(e) => Some(e),
_ => None,
}
}
pub fn is_unique_violation(&self) -> bool {
self.code() == Some("23505")
}
pub fn is_foreign_key_violation(&self) -> bool {
self.code() == Some("23503")
}
pub fn is_fatal(&self) -> bool {
matches!(self, Error::Io(_) | Error::ConnectionClosed | Error::Tls(_))
}
}
impl Error {
pub(crate) fn protocol(msg: impl Into<String>) -> Self {
Error::Protocol(msg.into())
}
pub(crate) fn server(
severity: String,
code: String,
message: String,
detail: Option<String>,
hint: Option<String>,
position: Option<u32>,
) -> Self {
Error::Server(Box::new(ServerError {
severity,
code,
message,
detail,
hint,
position,
}))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Severity {
Error,
Fatal,
Panic,
Warning,
Notice,
Debug,
Info,
Log,
}
impl fmt::Display for Severity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Severity::Error => write!(f, "ERROR"),
Severity::Fatal => write!(f, "FATAL"),
Severity::Panic => write!(f, "PANIC"),
Severity::Warning => write!(f, "WARNING"),
Severity::Notice => write!(f, "NOTICE"),
Severity::Debug => write!(f, "DEBUG"),
Severity::Info => write!(f, "INFO"),
Severity::Log => write!(f, "LOG"),
}
}
}