errcraft 0.1.0

Beautiful, structured, and colorful error handling for Rust.
Documentation
//! Integration with popular error handling and logging crates.

#[cfg(feature = "anyhow")]
#[cfg_attr(docsrs, doc(cfg(feature = "anyhow")))]
pub mod anyhow {
    //! Integration with the `anyhow` crate.

    use crate::ErrFrame;

    impl From<anyhow::Error> for ErrFrame {
        fn from(err: anyhow::Error) -> Self {
            ErrFrame::new(err.to_string()).context_text("Converted from anyhow::Error")
        }
    }

    // Note: From<ErrFrame> for anyhow::Error conflicts with anyhow's blanket impl
    // Users can convert manually: anyhow::Error::msg(err.to_string())
}

#[cfg(feature = "eyre")]
#[cfg_attr(docsrs, doc(cfg(feature = "eyre")))]
pub mod eyre_support {
    //! Integration with the `eyre` crate.

    use crate::ErrFrame;

    impl From<eyre::Report> for ErrFrame {
        fn from(err: eyre::Report) -> Self {
            ErrFrame::new(err.to_string()).context_text("Converted from eyre::Report")
        }
    }
}

#[cfg(feature = "tracing")]
#[cfg_attr(docsrs, doc(cfg(feature = "tracing")))]
pub mod tracing_support {
    //! Integration with the `tracing` crate.

    use crate::ErrFrame;
    use tracing::{error, warn};

    impl ErrFrame {
        /// Logs this error using `tracing::error!`.
        pub fn trace_error(&self) {
            error!(
                message = %self.message(),
                "Error occurred"
            );

            for layer in self.context_layers() {
                if let Some(key) = layer.key() {
                    error!(key = key, value = layer.value().as_str());
                }
            }

            #[cfg(feature = "std")]
            if let Some(source) = self.source.as_ref() {
                error!(source = %source, "Error source");
            }
        }

        /// Logs this error using `tracing::warn!`.
        pub fn trace_warn(&self) {
            warn!(
                message = %self.message(),
                "Warning occurred"
            );

            for layer in self.context_layers() {
                if let Some(key) = layer.key() {
                    warn!(key = key, value = layer.value().as_str());
                }
            }
        }
    }
}

#[cfg(feature = "axum")]
#[cfg_attr(docsrs, doc(cfg(feature = "axum")))]
pub mod axum_support {
    //! Integration with the `axum` web framework.

    use crate::ErrFrame;
    use axum::response::{IntoResponse, Response};
    use http::StatusCode;

    impl IntoResponse for ErrFrame {
        fn into_response(self) -> Response {
            #[cfg(feature = "serde")]
            {
                let json = serde_json::json!({
                    "error": self.message().to_string(),
                    "context": self.context_layers()
                        .filter_map(|layer| {
                            layer.key().map(|k| (k.to_string(), layer.value().as_str().to_string()))
                        })
                        .collect::<std::collections::HashMap<_, _>>(),
                });

                use axum::Json;
                (StatusCode::INTERNAL_SERVER_ERROR, Json(json)).into_response()
            }

            #[cfg(not(feature = "serde"))]
            {
                (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response()
            }
        }
    }
}

#[cfg(feature = "thiserror")]
#[cfg_attr(docsrs, doc(cfg(feature = "thiserror")))]
pub mod thiserror_support {
    //! Integration with the `thiserror` crate.
    //!
    //! This module provides conversions for errors that implement `std::error::Error`,
    //! which includes all `thiserror`-derived error types.

    // Note: The From<E: std::error::Error> impl in frame.rs already handles thiserror errors
}