bt_any_error 0.1.0

An easy-to-use, lightweight, zero-dependency generic Error that can be passed to another thread.
Documentation
use std::error::Error;
use std::fmt;

/// Unified error type used across the project.
pub type AnyErr = Box<dyn Error + Send + Sync + 'static>;

/// A generic, thread-safe error wrapper that preserves the original error's message.
pub struct StringError(String);

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

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

// Implement the standard Error trait so it can be cast to `Box<dyn StdError...>`
impl Error for StringError {}

/// Extension trait that converts any `Result<T, E>` into `Result<T, AnyError>`.
/// An extension trait to seamlessly upgrade non-Send errors into thread-safe `BoxError`s.
pub trait IntoAnyErr<T> {
    /// Converts the contained error into a generic thread-safe `BoxError` by transforming 
    /// it into a string representation if it isn't already thread-safe.    
    fn any(self) -> Result<T, AnyErr>;
}

// Implement the trait for ALL Results where the Error type implements Display.
// This catches plain Box<dyn Error>, strings, and concrete non-Send errors.
impl<T, E> IntoAnyErr<T> for Result<T, E>
where
    E: std::fmt::Display,
{
    #[inline]
    fn any(self) -> Result<T, AnyErr> {
        self.map_err(|e| {
            // Capture the original error as a String            
            // Converting the error to a String is guaranteed to be Send + Sync,
            // and then turn that String into our boxed error type.
            let string_error = e.to_string();
            // Wrap it in the Send+Sync StringError
            let generic_err = StringError(string_error);            
            Box::new(generic_err) as AnyErr
        })
    }
}