toobad 0.1.0

A simple, intuitive error handling library
Documentation
//! Error type and construction utilities.
//!
//! This module provides the core [`Error`] type that serves as the central error container
//! for the crate. It wraps any error implementing `Error + Send + Sync + 'static` using
//! type erasure, allowing uniform error handling throughout an application.
//!
//! The module also provides the [`Error::msg`] constructor for creating simple errors from
//! string messages and the [`Error::downcast`] method for introspecting wrapped errors.

/// Internal wrapper for boxed dynamic errors.
///
/// This struct holds a heap-allocated, dynamically dispatched error that is both
/// sendable and syncable across thread boundaries.
#[derive(Debug)]
pub(crate) struct Inner(pub(crate) Box<dyn std::error::Error + Send + Sync>);

/// Unified error type that wraps any `Error + Send + Sync`.
///
/// This is the primary error type exposed by the crate. It wraps any error implementing
/// `Error + Send + Sync + 'static`, allowing for ergonomic error handling across
/// the entire application. The underlying error can be retrieved via the [`Error::source`] method
/// or downcast to its concrete type.
pub struct Error(pub(crate) Inner);

impl std::fmt::Debug for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", &*self.0 .0)
    }
}

impl<E: std::error::Error + Send + Sync + 'static> From<E> for Error {
    fn from(e: E) -> Self {
        Self(Inner(Box::new(e)))
    }
}

impl Error {
    /// Create a new error from any error type implementing `Error + Send + Sync + 'static`.
    ///
    /// This method wraps the given error into the unified [`Error`] type. It is equivalent
    /// to using `From::from` directly.
    pub fn new<E: std::error::Error + Send + Sync + 'static>(error: E) -> Self {
        Self::from(error)
    }

    /// Retrieve the underlying source error.
    ///
    /// Returns a trait object pointing to the wrapped error, enabling inspection
    /// or further downcasting by the caller.
    pub fn source(&self) -> &(dyn std::error::Error + 'static) {
        &*self.0 .0
    }

    /// Create an error from a plain message string.
    ///
    /// This is useful for creating simple errors without defining a custom error type.
    /// The message is wrapped in an internal error type and can be retrieved via [`Error::source`].
    pub fn msg(message: impl Into<String>) -> Self {
        Self::new(MessageError(message.into()))
    }

    /// Attempt to downcast the wrapped error to a specific type.
    ///
    /// Returns `Some` if the underlying error is of type `E`, otherwise returns `None`.
    /// This allows introspection and specific error handling when the error type is known.
    pub fn downcast<E: std::error::Error + 'static>(&self) -> Option<&E> {
        self.0 .0.downcast_ref::<E>()
    }
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", &*self.0 .0)
    }
}

/// Internal error type for plain string messages.
///
/// This struct wraps a `String` and implements the `Error` trait, enabling it to be
/// used with the [`Error::msg`] constructor.
#[derive(Debug)]
struct MessageError(String);

impl std::fmt::Display for MessageError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl std::error::Error for MessageError {}