apollo-errors 0.7.0

Structured error handling with automatic format conversion
Documentation
//! Error registry for introspection and catalog generation.
//!
//! Use [`error_metadata()`] to list all registered error types in your application.
//! This is useful for generating documentation or API specifications.

use crate::metadata::{ErrorMetadata, FormatConfig};
use std::any::TypeId;
use std::error::Error as StdError;

/// Function pointer type for rendering an error to JSON
///
/// Takes a `&(dyn Error + 'static)` and uses `downcast_ref` to get the concrete type.
/// Returns None if the error is not of the expected type.
/// Returns Some(Err) if serialization fails.
pub type JsonRenderFn = fn(
    &(dyn StdError + 'static),
    FormatConfig,
) -> Option<Result<serde_json::Value, serde_json::Error>>;

/// Function pointer type for rendering an error to HTML
pub type HtmlRenderFn = fn(&(dyn StdError + 'static), FormatConfig) -> Option<String>;

/// Function pointer type for rendering an error to GraphQL format
/// Returns None if the error is not of the expected type.
/// Returns Some(Err) if serialization fails.
pub type GraphqlRenderFn = fn(
    &(dyn StdError + 'static),
    FormatConfig,
) -> Option<Result<serde_json::Value, serde_json::Error>>;

/// Function pointer type for rendering an error to plain text
pub type TextRenderFn = fn(&(dyn StdError + 'static), FormatConfig) -> Option<String>;

/// Function pointer type for getting the HTTP status code from an error
pub type HttpStatusFn = fn(&(dyn StdError + 'static)) -> Option<http::StatusCode>;

/// Function pointer type for rendering an error to JSON-RPC format
/// Returns None if the error is not of the expected type.
/// Returns Some(Err) if serialization fails.
pub type JsonRpcRenderFn = fn(
    &(dyn StdError + 'static),
    FormatConfig,
) -> Option<Result<serde_json::Value, serde_json::Error>>;

/// Function pointer type for getting HTTP headers from an error
pub type HttpHeadersFn =
    fn(&(dyn StdError + 'static)) -> Option<Vec<(http::HeaderName, http::HeaderValue)>>;

/// Registry entry for each error type
pub struct ErrorRegistryEntry {
    /// TypeId of the error type for fast matching
    pub type_id: TypeId,

    /// Human-readable type name for debugging
    pub type_name: &'static str,

    /// Metadata about the error type (codes, messages, fields, etc.)
    pub metadata: &'static ErrorMetadata,

    /// Render to JSON format (serde_json::Value)
    pub render_json: JsonRenderFn,

    /// Render to HTML format (String)
    pub render_html: HtmlRenderFn,

    /// Render to GraphQL format (serde_json::Value)
    pub render_graphql: GraphqlRenderFn,

    /// Render to plain text format (String)
    pub render_text: TextRenderFn,

    /// Get HTTP status code from error instance
    pub http_status: HttpStatusFn,

    /// Render to JSON-RPC format (serde_json::Value)
    pub render_jsonrpc: JsonRpcRenderFn,

    /// Get HTTP headers from error instance
    pub http_headers: HttpHeadersFn,
}

/// Distributed slice holding all registered errors
///
/// Each error type adds an entry to this slice at compile time via linkme.
///
/// Note: This is an internal implementation detail. Users should use [`error_metadata()`]
/// instead, which provides the same data in a more stable API.
#[doc(hidden)]
#[linkme::distributed_slice]
pub static ERROR_REGISTRY: [ErrorRegistryEntry] = [..];

/// Generic helper to render an error using a specific render function
fn render_with<T>(f: impl Fn(&ErrorRegistryEntry) -> Option<T>) -> Option<T> {
    for entry in ERROR_REGISTRY {
        if let Some(result) = f(entry) {
            return Some(result);
        }
    }
    None
}

/// Render an error to JSON format
pub(crate) fn render_json(
    error: &(dyn StdError + 'static),
    config: FormatConfig,
) -> Option<Result<serde_json::Value, serde_json::Error>> {
    render_with(|entry| (entry.render_json)(error, config))
}

/// Render an error to HTML format
pub(crate) fn render_html(
    error: &(dyn StdError + 'static),
    config: FormatConfig,
) -> Option<String> {
    render_with(|entry| (entry.render_html)(error, config))
}

/// Render an error to GraphQL format
pub(crate) fn render_graphql(
    error: &(dyn StdError + 'static),
    config: FormatConfig,
) -> Option<Result<serde_json::Value, serde_json::Error>> {
    render_with(|entry| (entry.render_graphql)(error, config))
}

/// Render an error to plain text format
pub(crate) fn render_text(
    error: &(dyn StdError + 'static),
    config: FormatConfig,
) -> Option<String> {
    render_with(|entry| (entry.render_text)(error, config))
}

/// Get HTTP status code from an error instance
pub(crate) fn http_status(error: &(dyn StdError + 'static)) -> Option<http::StatusCode> {
    render_with(|entry| (entry.http_status)(error))
}

/// Render an error to JSON-RPC format
pub(crate) fn render_jsonrpc(
    error: &(dyn StdError + 'static),
    config: FormatConfig,
) -> Option<Result<serde_json::Value, serde_json::Error>> {
    render_with(|entry| (entry.render_jsonrpc)(error, config))
}

/// Get HTTP headers from an error instance
pub(crate) fn http_headers(
    error: &(dyn StdError + 'static),
) -> Option<Vec<(http::HeaderName, http::HeaderValue)>> {
    render_with(|entry| (entry.http_headers)(error))
}

/// Get all registered error registry entries
///
/// Returns a slice of all error types registered in the binary.
/// This is useful for generating error catalogs, documentation, or API specifications.
///
/// # Example
///
/// ```ignore
/// use apollo_errors::error_metadata;
///
/// for entry in error_metadata() {
///     println!("Type: {}", entry.type_name);
///     println!("Metadata: {:?}", entry.metadata);
/// }
/// ```
pub fn error_metadata() -> &'static [ErrorRegistryEntry] {
    &ERROR_REGISTRY
}