use core::fmt;
use crate::ffi;
mod category;
mod code;
mod detail;
mod integration;
mod location;
mod reason;
use detail::{ErrorDetail, ErrorInner};
pub use category::ErrorCategory;
pub use code::ErrorCode;
pub use integration::{ErrorContainer, IntegrationError};
pub use location::ErrorLocation;
pub use reason::{
AbortError, AuthorizationError, BusyError, CantOpenError, ConstraintError, CorruptError,
ErrorReason, FetchError, GeneralError, IoError, LockedError, ParameterError, ReadOnlyError,
RowError, TextEncodingError,
};
pub type Result<T, E = Error> = core::result::Result<T, E>;
#[derive(Clone)]
pub struct Error {
inner: Box<ErrorInner>,
}
impl Error {
#[cold]
pub fn new(code: ErrorCode) -> Self {
Self {
inner: Box::new(ErrorInner::new(code)),
}
}
#[cold]
pub(crate) fn with_detail(code: ErrorCode, detail: impl Into<ErrorDetail>) -> Self {
Self {
inner: Box::new(ErrorInner::with_detail(code, detail.into())),
}
}
#[cold]
#[inline(never)]
pub(crate) fn from_code(code: i32) -> Option<Self> {
ErrorCode::new(code).map(Self::new)
}
#[cold]
#[inline(never)]
pub(crate) fn from_connection(source: impl ffi::Connected, code: i32) -> Option<Self> {
ErrorCode::new(code).map(|code| match ErrorDetail::extract(source, code) {
Some(detail) => Self::with_detail(code, detail),
None => Self::new(code),
})
}
#[cold]
#[inline(never)]
pub(crate) fn from_prepare(source: impl ffi::Connected, code: i32) -> Option<Self> {
ErrorCode::new(code).map(
|code| match ErrorDetail::extract_with_location(source, code) {
Some(detail) => Self::with_detail(code, detail),
None => Self::new(code),
},
)
}
#[allow(dead_code, unreachable_code)]
#[cold]
#[inline(never)]
pub(crate) fn from_bind(source: impl Into<IntegrationError>) -> Self {
Self::with_detail(ErrorCode::SQUIRE_PARAMETER_BIND, source.into())
}
#[allow(dead_code, unreachable_code)]
#[cold]
#[inline(never)]
pub(crate) fn from_fetch(source: impl Into<IntegrationError>) -> Self {
Self::with_detail(ErrorCode::SQUIRE_FETCH_PARSE, source.into())
}
#[cold]
#[inline(never)]
pub(crate) fn row_not_returned() -> Self {
Self::new(ErrorCode::SQUIRE_ROW_NOT_RETURNED)
}
pub const fn code(&self) -> ErrorCode {
self.inner.code
}
pub const fn category(&self) -> Option<ErrorCategory> {
ErrorCategory::from_code(self.code())
}
pub const fn reason(&self) -> Option<ErrorReason> {
ErrorReason::from_code(self.code())
}
pub const fn is_sqlite(&self) -> bool {
self.code().is_sqlite()
}
pub const fn is_squire(&self) -> bool {
self.code().is_squire()
}
pub const fn is_integration(&self) -> bool {
matches!(self.detail(), Some(ErrorDetail::Integration(_)))
}
pub const fn as_integration(&self) -> Option<&IntegrationError> {
match self.detail() {
Some(ErrorDetail::Integration(error)) => Some(error),
_ => None,
}
}
pub const fn source_location(&self) -> Option<ErrorLocation> {
match self.detail() {
Some(ErrorDetail::SourceMessage(_, location)) => Some(*location),
_ => None,
}
}
const fn detail(&self) -> Option<&ErrorDetail> {
self.inner.detail.as_ref()
}
const fn message(&self) -> Option<&str> {
match self.detail() {
Some(ErrorDetail::Message(message)) => Some(message.as_str()),
Some(ErrorDetail::SourceMessage(message, _)) => Some(message.as_str()),
_ => None,
}
}
}
impl Default for Error {
fn default() -> Self {
Self::new(ErrorCode::default())
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let code = self::code::ErrorCodeName(self.code());
let message = self.message().unwrap_or_else(|| self.code().description());
let mut tuple = f.debug_tuple("Error");
tuple.field(&code);
tuple.field(&message);
if let Some(location) = self.source_location() {
tuple.field(&location);
}
if let Some(integration) = self.as_integration() {
tuple.field(&integration);
}
tuple.finish()
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_sqlite() {
let code = self.code();
let message = self.message().unwrap_or_else(|| self.code().description());
write!(f, "{message} [{code}]")
} else {
let description = self.code().description();
match self.message() {
Some(message) => write!(f, "{description}: {message}"),
None => write!(f, "{description}"),
}
}
}
}
impl core::error::Error for Error {
fn description(&self) -> &str {
self.code().description()
}
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
if let Some(integration) = self.as_integration() {
match *integration {
#[cfg(feature = "chrono")]
IntegrationError::Chrono(ref error) => Some(error),
#[cfg(feature = "jiff")]
IntegrationError::Jiff(ref error) => Some(error),
#[cfg(all(feature = "serde", feature = "json"))]
IntegrationError::Json(ref container) => Some(container.as_ref()),
#[cfg(all(feature = "serde", feature = "jsonb"))]
IntegrationError::Jsonb(ref container) => Some(container.as_ref()),
#[cfg(feature = "url")]
IntegrationError::Url(ref error) => Some(error),
#[cfg(feature = "uuid")]
IntegrationError::Uuid(ref bx) => Some(bx.as_ref()),
}
} else {
None
}
}
}
impl From<core::str::Utf8Error> for Error {
fn from(_: core::str::Utf8Error) -> Self {
Self::from(reason::TextEncodingError::InvalidUtf8)
}
}
impl From<core::ffi::FromBytesWithNulError> for Error {
fn from(_: core::ffi::FromBytesWithNulError) -> Self {
Self::from(ErrorCategory::TextEncoding)
}
}
impl From<i32> for Error {
#[cold]
fn from(code: i32) -> Self {
Error::from_code(code).unwrap_or_default()
}
}
impl From<ErrorCategory> for Error {
#[cold]
fn from(category: ErrorCategory) -> Self {
Error::new(category.code())
}
}
impl<T> From<T> for Error
where
ErrorReason: From<T>,
{
#[cold]
fn from(value: T) -> Self {
Error::new(ErrorReason::from(value).code())
}
}