use core::{fmt, ops::Deref};
#[cfg(not(feature = "multi-thread"))]
use std::rc::Rc;
#[cfg(feature = "multi-thread")]
use std::sync::Arc;
#[derive(Clone, Debug)]
pub enum IntegrationError {
#[cfg(feature = "chrono")]
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
Chrono(chrono::ParseError),
#[cfg(feature = "jiff")]
#[cfg_attr(docsrs, doc(cfg(feature = "jiff")))]
Jiff(jiff::Error),
#[cfg(all(feature = "serde", feature = "json"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "serde", feature = "json"))))]
Json(ErrorContainer<squire_serde::json::Error>),
#[cfg(all(feature = "serde", feature = "jsonb"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "serde", feature = "jsonb"))))]
Jsonb(ErrorContainer<squire_serde::jsonb::Error>),
#[cfg(feature = "url")]
#[cfg_attr(docsrs, doc(cfg(feature = "url")))]
Url(url::ParseError),
#[cfg(feature = "uuid")]
#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
Uuid(Box<uuid::Error>),
}
#[cfg(feature = "chrono")]
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
impl IntegrationError {
pub fn is_chrono(&self) -> bool {
matches!(self, Self::Chrono(_))
}
pub fn as_chrono(&self) -> Option<&chrono::ParseError> {
match self {
Self::Chrono(error) => Some(error),
_ => None,
}
}
}
#[cfg(feature = "chrono")]
#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
impl From<chrono::ParseError> for IntegrationError {
fn from(error: chrono::ParseError) -> Self {
Self::Chrono(error)
}
}
#[cfg(feature = "jiff")]
#[cfg_attr(docsrs, doc(cfg(feature = "jiff")))]
impl IntegrationError {
pub fn is_jiff(&self) -> bool {
matches!(self, Self::Jiff(_))
}
pub fn as_jiff(&self) -> Option<&jiff::Error> {
match self {
Self::Jiff(error) => Some(error),
_ => None,
}
}
}
#[cfg(feature = "jiff")]
#[cfg_attr(docsrs, doc(cfg(feature = "jiff")))]
impl From<jiff::Error> for IntegrationError {
fn from(error: jiff::Error) -> Self {
Self::Jiff(error)
}
}
#[cfg(all(feature = "serde", feature = "json"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "serde", feature = "json"))))]
impl IntegrationError {
pub fn is_json(&self) -> bool {
matches!(self, Self::Json(_))
}
pub fn as_json(&self) -> Option<&squire_serde::json::Error> {
match self {
Self::Json(container) => Some(container.as_ref()),
_ => None,
}
}
}
#[cfg(all(feature = "serde", feature = "json"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "serde", feature = "json"))))]
impl From<squire_serde::json::Error> for IntegrationError {
fn from(error: squire_serde::json::Error) -> Self {
Self::Json(ErrorContainer::new(error))
}
}
#[cfg(all(feature = "serde", feature = "jsonb"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "serde", feature = "jsonb"))))]
impl IntegrationError {
pub fn is_jsonb(&self) -> bool {
matches!(self, Self::Jsonb(_))
}
pub fn as_jsonb(&self) -> Option<&squire_serde::jsonb::Error> {
match self {
Self::Jsonb(container) => Some(container.as_ref()),
_ => None,
}
}
}
#[cfg(all(feature = "serde", feature = "jsonb"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "serde", feature = "jsonb"))))]
impl From<squire_serde::jsonb::Error> for IntegrationError {
fn from(error: squire_serde::jsonb::Error) -> Self {
Self::Jsonb(ErrorContainer::new(error))
}
}
#[cfg(feature = "url")]
#[cfg_attr(docsrs, doc(cfg(feature = "url")))]
impl IntegrationError {
pub fn is_url(&self) -> bool {
matches!(self, Self::Url(_))
}
pub fn as_url(&self) -> Option<&url::ParseError> {
match self {
Self::Url(error) => Some(error),
_ => None,
}
}
}
#[cfg(feature = "url")]
#[cfg_attr(docsrs, doc(cfg(feature = "url")))]
impl From<url::ParseError> for IntegrationError {
fn from(error: url::ParseError) -> Self {
Self::Url(error)
}
}
#[cfg(feature = "uuid")]
#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
impl IntegrationError {
pub fn is_uuid(&self) -> bool {
matches!(self, Self::Uuid(_))
}
pub fn as_uuid(&self) -> Option<&uuid::Error> {
match self {
Self::Uuid(bx) => Some(bx.as_ref()),
_ => None,
}
}
}
#[cfg(feature = "uuid")]
#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
impl From<uuid::Error> for IntegrationError {
fn from(error: uuid::Error) -> Self {
Self::Uuid(Box::new(error))
}
}
impl fmt::Display for IntegrationError {
fn fmt(&self, #[allow(unused_variables)] f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
#[cfg(feature = "chrono")]
IntegrationError::Chrono(ref error) => error.fmt(f),
#[cfg(feature = "jiff")]
IntegrationError::Jiff(ref error) => error.fmt(f),
#[cfg(all(feature = "serde", feature = "json"))]
IntegrationError::Json(ErrorContainer(ref error)) => error.fmt(f),
#[cfg(all(feature = "serde", feature = "jsonb"))]
IntegrationError::Jsonb(ErrorContainer(ref error)) => error.fmt(f),
#[cfg(feature = "url")]
IntegrationError::Url(ref error) => error.fmt(f),
#[cfg(feature = "uuid")]
IntegrationError::Uuid(ref bx) => bx.fmt(f),
}
}
}
impl core::error::Error for IntegrationError {}
#[cfg(not(feature = "multi-thread"))]
#[derive(PartialEq, Eq)]
pub struct ErrorContainer<T>(pub Rc<T>);
#[cfg(feature = "multi-thread")]
#[derive(PartialEq, Eq)]
pub struct ErrorContainer<T>(pub Arc<T>);
impl<T> ErrorContainer<T> {
#[cfg_attr(not(feature = "multi-thread"), doc = "Wrap an error in [`Rc`].")]
#[cfg_attr(feature = "multi-thread", doc = "Wrap an error in [`Arc`].")]
pub fn new(error: T) -> Self {
#[cfg(not(feature = "multi-thread"))]
{
Self(Rc::new(error))
}
#[cfg(feature = "multi-thread")]
{
Self(Arc::new(error))
}
}
}
impl<T> AsRef<T> for ErrorContainer<T> {
fn as_ref(&self) -> &T {
self.0.as_ref()
}
}
impl<T> Deref for ErrorContainer<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}
impl<T> From<T> for ErrorContainer<T> {
fn from(value: T) -> Self {
Self::new(value)
}
}
impl<T> Clone for ErrorContainer<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T: fmt::Debug> fmt::Debug for ErrorContainer<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.as_ref().fmt(f)
}
}
impl<T: fmt::Display> fmt::Display for ErrorContainer<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.as_ref().fmt(f)
}
}
impl<T: core::error::Error> core::error::Error for ErrorContainer<T> {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn integration_error_size() {
assert!(
size_of::<IntegrationError>() <= 2 * size_of::<usize>(),
"size of IntegrationError ({}) should be ≤ 2 words",
size_of::<IntegrationError>(),
);
}
}