oxiderr 0.1.0

Streamlining Error Handling in Rust
Documentation
use crate::ErrorKind;
use serde::Serialize;

/// Trait representing a structured error with categorized information.
///
/// The `AsError` trait provides a standardized way to define and retrieve
/// detailed error information, including its kind, message, classification,
/// and additional details.
///
/// Implementing this trait allows for better error management and debugging
/// by ensuring consistency in error structures.
///
pub trait AsError {
    /// The kind of error.
    ///
    /// The error kind represents a categorized type of error, allowing
    /// consumers to handle different error types distinctly.
    fn kind() -> ErrorKind;

    /// The error message.
    ///
    /// This message provides a human-readable description of the error.
    fn message(&self) -> String;

    /// The class or category of the error.
    ///
    /// The class helps in further classifying errors, providing an additional
    /// layer of categorization beyond `ErrorKind`.
    fn class(&self) -> String;

    /// Additional details related to the error.
    ///
    /// This method provides an optional key-value mapping of extra data
    /// associated with the error, which can be useful for debugging
    /// or logging purposes.
    fn details(&self) -> Option<std::collections::BTreeMap<String, serde_value::Value>>;
}

/// A structured error type with categorized information.
///
/// The `Error` struct represents an error with a specific kind, classification,
/// message, and optional additional details.
///
/// This structure is designed to facilitate error handling by providing
/// detailed information that can be logged or displayed.
///
#[derive(Debug, Clone, Serialize)]
pub struct Error {
    /// The kind of error.
    ///
    /// This field categorizes the error, allowing distinct handling based on
    /// its type. It is skipped during serialization.
    #[serde(skip_serializing)]
    pub kind: ErrorKind,

    /// The class or category of the error.
    ///
    /// This helps further classify the error beyond its kind.
    pub class: String,

    /// A human-readable message describing the error.
    pub message: String,

    /// Additional details related to the error.
    ///
    /// This optional field contains extra context in a key-value format,
    /// which can be useful for debugging or logging purposes.
    pub details: Option<std::collections::BTreeMap<String, serde_value::Value>>,
}

/// Converts any type implementing `AsError` into an `Error` instance.
///
/// This implementation allows seamless conversion from custom error types
/// that implement `AsError` into the `Error` struct, preserving structured
/// error information.
///
/// # Type Parameters
/// - `E`: A type that implements the `AsError` trait.
///
/// # Example
/// ```rust
/// let custom_error = MyCustomError::new();
/// let error: Error = custom_error.into();
/// ```
impl<E: AsError> From<E> for Error {
    fn from(value: E) -> Self {
        Error {
            kind: E::kind(),
            class: value.class(),
            message: value.message(),
            details: value.details(),
        }
    }
}

/// Converts an `Error` into a `std::io::Error`.
///
/// This implementation maps an `Error` to an `std::io::Error` using the
/// `InvalidData` error kind and formats the error message accordingly.
/// This allows for seamless integration with Rust's standard I/O error handling.
///
/// # Example
/// ```rust
/// let custom_error = Error::new(...);
/// let io_error: std::io::Error = custom_error.into();
/// ```
impl From<Error> for std::io::Error {
    fn from(e: Error) -> Self {
        std::io::Error::new(std::io::ErrorKind::InvalidData, format!("{}", e))
    }
}

/// Implements the `Display` trait for `Error`.
///
/// This implementation formats the error as a human-readable string,
/// including its kind, class, code, and message. It provides a structured
/// error output that can be useful for logging or displaying errors in a UI.
///
/// # Format
/// ```text
/// [message_id] class (code) - message
/// ```
///
/// # Example
/// ```rust
/// let error = Error::new(...);
/// println!("{}", error);
/// ```
impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}",
            format!(
                "[{}] {} ({}) - {}",
                self.kind.message_id(),
                self.class,
                self.kind.code(),
                self.message
            )
        )
    }
}