apollo-errors-derive 0.4.0

Proc macro for deriving apollo-errors::Error trait
Documentation
//! Shared helper functions for code generation

use proc_macro2::TokenStream;
use quote::quote;

use crate::ir::FieldDefinition;

/// Default JSON-RPC error code when not specified (-32000 = "Server error")
pub(crate) const DEFAULT_JSONRPC_CODE: i32 = -32000;

/// Generate a token stream for an Option<String> field
///
/// Returns `Some("value")` or `None` as appropriate.
pub(crate) fn quote_option_str(opt: &Option<String>) -> TokenStream {
    match opt {
        Some(v) => quote! { Some(#v) },
        None => quote! { None },
    }
}

/// Generate downcast attempts for registry render functions
///
/// This generates the downcast chain that tries to match the error type
/// in various wrapper forms (direct, Box, Arc, Box<Arc>).
pub(crate) fn generate_downcast_attempts(name: &syn::Ident, method_name: &str) -> TokenStream {
    let method_ident = syn::Ident::new(method_name, name.span());
    quote! {
        if let Some(concrete) = error.downcast_ref::<#name>() {
            return Some(::apollo_errors::Error::#method_ident(concrete));
        }
        if let Some(boxed) = error.downcast_ref::<Box<#name>>() {
            return Some(::apollo_errors::Error::#method_ident(boxed.as_ref()));
        }
        if let Some(arc) = error.downcast_ref::<std::sync::Arc<#name>>() {
            return Some(::apollo_errors::Error::#method_ident(arc.as_ref()));
        }
        if let Some(box_arc) = error.downcast_ref::<Box<std::sync::Arc<#name>>>() {
            return Some(::apollo_errors::Error::#method_ident(box_arc.as_ref().as_ref()));
        }
        None
    }
}

/// Generate field metadata tokens for a single field
pub(crate) fn generate_field_metadata(field: &FieldDefinition) -> TokenStream {
    let rust_name = field.rust_name.to_string();
    let output_name = &field.output_name;
    let ty = &field.ty;
    let ty_str = quote!(#ty).to_string();
    let is_extension = field.is_extension;
    let http_header = quote_option_str(&field.http_header);

    quote! {
        ::apollo_errors::private::FieldMetadata {
            rust_name: #rust_name,
            output_name: #output_name,
            ty: #ty_str,
            is_extension: #is_extension,
            http_header: #http_header,
        }
    }
}

/// Generate code to extract HTTP headers from fields
///
/// When `use_self_prefix` is true, generates `self.field` access (for structs).
/// When false, generates just `field` access (for enum match patterns).
pub(crate) fn generate_http_headers_body(
    fields: &[FieldDefinition],
    use_self_prefix: bool,
) -> TokenStream {
    let header_fields: Vec<_> = fields
        .iter()
        .filter_map(|f| f.http_header.as_ref().map(|h| (f, h)))
        .collect();

    if header_fields.is_empty() {
        quote! { Vec::new() }
    } else {
        let field_extractions: Vec<_> = header_fields
            .iter()
            .map(|(field, header_name)| {
                let rust_name = &field.rust_name;
                let header_name_lower = header_name.to_ascii_lowercase();

                let (field_access, field_ref) = if use_self_prefix {
                    (quote! { self.#rust_name }, quote! { &self.#rust_name })
                } else {
                    (quote! { #rust_name }, quote! { #rust_name })
                };

                if field.is_option {
                    quote! {
                        if let Some(__apollo_inner) = #field_access.as_ref() {
                            if let Some(__apollo_val) = ::apollo_errors::private::ToHeaderValue::to_header_value(__apollo_inner) {
                                __apollo_headers.push((
                                    ::apollo_errors::http::HeaderName::from_static(#header_name_lower),
                                    __apollo_val,
                                ));
                            }
                        }
                    }
                } else {
                    quote! {
                        if let Some(__apollo_val) = ::apollo_errors::private::ToHeaderValue::to_header_value(#field_ref) {
                            __apollo_headers.push((
                                ::apollo_errors::http::HeaderName::from_static(#header_name_lower),
                                __apollo_val,
                            ));
                        }
                    }
                }
            })
            .collect();

        quote! {
            {
                let mut __apollo_headers = Vec::new();
                #(#field_extractions)*
                __apollo_headers
            }
        }
    }
}