apollo-errors 0.7.0

Structured error handling with automatic format conversion
Documentation
//! Core Error trait definition

use crate::metadata::FormatConfig;

/// Extract the diagnostic code from a `miette::Diagnostic` as a string.
///
/// Returns `"UNKNOWN_ERROR"` if no code is present.
#[inline]
pub fn diagnostic_code<D: miette::Diagnostic + ?Sized>(diagnostic: &D) -> String {
    diagnostic
        .code()
        .map(|c| c.to_string())
        .unwrap_or_else(|| "UNKNOWN_ERROR".to_string())
}

/// Extract the help text from a `miette::Diagnostic` as an optional string.
#[inline]
pub fn diagnostic_help<D: miette::Diagnostic + ?Sized>(diagnostic: &D) -> Option<String> {
    diagnostic.help().map(|h| h.to_string())
}

/// The core trait for apollo errors.
///
/// This trait extends `std::error::Error` and `miette::Diagnostic`
/// to provide multi-format rendering.
///
/// # Derive Macro
///
/// Use `#[derive(apollo_errors::Error)]` to automatically implement this trait.
/// The derive macro generates format renderers for JSON, GraphQL, HTML, and text output.
///
/// # Example
///
/// ```ignore
/// use apollo_errors::Error;
///
/// #[derive(Debug, Error)]
/// pub enum MyError {
///     #[error("Something went wrong")]
///     #[diagnostic(code(my_product::component::error_name))]
///     SomethingWrong {
///         #[extension]
///         field: String,
///     },
/// }
/// ```
pub trait Error: std::error::Error + miette::Diagnostic {
    /// Render this error as JSON format
    ///
    /// `config` controls field name casing and error code format in the output.
    ///
    /// Returns a serde_json::Value with:
    /// - `error`: The error code
    /// - `message`: The error message
    /// - Additional fields from `#[extension]` fields
    ///
    /// # Errors
    ///
    /// Returns an error if any field fails to serialize to JSON
    fn to_json(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error>;

    /// Render this error in debug format using Rust's standard `Debug` output.
    fn to_debug(&self) -> String
    where
        Self: std::fmt::Debug,
    {
        format!("{self:?}")
    }

    /// Render this error as HTML
    ///
    /// `config` controls field name casing and error code format in the output.
    /// Returns an HTML representation suitable for browser display.
    fn to_html(&self, config: FormatConfig) -> String;

    /// Render this error as GraphQL JSON format
    ///
    /// `config` controls field name casing and error code format in the output.
    ///
    /// Returns a serde_json::Value with:
    /// - `message`: The error message
    /// - `extensions`: Additional structured data from `#[extension]` fields
    /// - `code`: The diagnostic code (e.g., APOLLO_PRODUCT_COMPONENT_ERROR)
    ///
    /// # Errors
    ///
    /// Returns an error if any field fails to serialize to JSON
    fn to_graphql(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error>;

    /// Render this error as plain text
    ///
    /// `config` controls field name casing and error code format in the output.
    /// Returns a human-readable plain text representation.
    fn to_text(&self, config: FormatConfig) -> String;

    /// Render this error as JSON-RPC 2.0 error format
    ///
    /// `config` controls field name casing and error code format in the output.
    ///
    /// Returns a serde_json::Value with:
    /// - `code`: Integer error code (from `#[jsonrpc_code(...)]` or default -32000)
    /// - `message`: The error message
    /// - `data`: Object containing `diagnostic_code` and any `#[extension]` fields
    ///
    /// # Errors
    ///
    /// Returns an error if any field fails to serialize to JSON
    fn to_jsonrpc(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error>;

    /// Get the HTTP status code for this error
    ///
    /// Returns the appropriate HTTP status code (e.g., 400, 404, 500).
    /// Defaults to 500 if not specified.
    fn http_status(&self) -> http::StatusCode;

    /// Get HTTP headers that should be returned with this error
    ///
    /// Returns header name-value pairs from fields marked with `#[http_header("...")]`.
    /// For Option fields, headers are only included if the value is Some.
    fn http_headers(&self) -> Vec<(http::HeaderName, http::HeaderValue)>;
}