waddling-errors-macros 0.7.3

Procedural macros for structured error codes with compile-time validation and taxonomy enforcement
Documentation
//! Implementation of the `doc_gen!` helper macro
//!
//! This macro generates registration code for diagnostics that should be included
//! in documentation generation.

use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use quote::quote;
use syn::{
    LitStr, Result, Token,
    parse::{Parse, ParseStream},
    punctuated::Punctuated,
};

/// Parse: E.Component.Primary.SEQUENCE => ["json", "html"]
struct DocGenEntry {
    severity: Ident,
    _dot1: Token![.],
    component: Ident,
    _dot2: Token![.],
    primary: Ident,
    _dot3: Token![.],
    sequence: Ident,
    _arrow: Token![=>],
    _bracket_open: syn::token::Bracket,
    formats: Punctuated<LitStr, Token![,]>,
}

impl Parse for DocGenEntry {
    fn parse(input: ParseStream) -> Result<Self> {
        let severity = input.parse()?;
        let _dot1 = input.parse()?;
        let component = input.parse()?;
        let _dot2 = input.parse()?;
        let primary = input.parse()?;
        let _dot3 = input.parse()?;
        let sequence = input.parse()?;
        let _arrow = input.parse()?;

        let content;
        let _bracket_open = syn::bracketed!(content in input);
        let formats = Punctuated::parse_terminated(&content)?;

        Ok(DocGenEntry {
            severity,
            _dot1,
            component,
            _dot2,
            primary,
            _dot3,
            sequence,
            _arrow,
            _bracket_open,
            formats,
        })
    }
}

pub fn expand(input: TokenStream) -> TokenStream {
    let entry = syn::parse_macro_input!(input as DocGenEntry);

    // Generate constant name: E_COMPONENT_PRIMARY_SEQUENCE_COMPLETE
    let const_name = Ident::new(
        &format!(
            "{}_{}_{}_{}_COMPLETE",
            entry.severity.to_string().to_uppercase(),
            entry.component.to_string().to_uppercase(),
            entry.primary.to_string().to_uppercase(),
            entry.sequence.to_string().to_uppercase()
        ),
        Span::call_site(),
    );

    // Extract format names
    let format_names: Vec<String> = entry.formats.iter().map(|lit| lit.value()).collect();
    let formats_array = format_names.iter().map(|s| s.as_str());

    // Generate diagnostic code string for documentation
    let diagnostic_code = format!(
        "{}.{}.{}.{}",
        entry.severity, entry.component, entry.primary, entry.sequence
    );

    // Generate unique static variable name for registration entry
    let registry_entry_name = Ident::new(
        &format!("__DOC_GEN_ENTRY_{}", const_name.to_string().to_uppercase()),
        Span::call_site(),
    );

    let output = quote! {
        // Generate a registration entry that can be collected later
        #[cfg(feature = "metadata")]
        #[doc = concat!("Documentation generation entry for ", #diagnostic_code)]
        pub struct #registry_entry_name;

        #[cfg(feature = "metadata")]
        impl #registry_entry_name {
            /// Register this diagnostic with a DocRegistry
            pub fn register(registry: &mut ::waddling_errors::doc_generator::DocRegistry) {
                registry.register_diagnostic_complete(&#const_name);
            }

            /// Get the formats requested for this diagnostic
            pub const fn formats() -> &'static [&'static str] {
                &[#(#formats_array),*]
            }

            /// Get the diagnostic code
            pub const fn code() -> &'static str {
                #diagnostic_code
            }
        }
    };

    TokenStream::from(output)
}