elastic-mapping-macro 0.1.0

Procedural macros for elastic-mapping - generate Elasticsearch mappings from Rust types
Documentation
use crate::{
    codegen::{gen_string_mapping, IterableProperties},
    error::GeneratorError,
    rename::RenameCasing,
    DocumentVariant, MappingProperty, OBJECT_TYPE, PROPERTIES_FIELD, TYPE_FIELD,
};

/// Generates mapping for adjacently tagged enums (#[serde(tag = "...", content = "...")])
pub(crate) fn gen_adjacently_tagged_enum(
    tag: &str,
    content: &str,
    variants: &[DocumentVariant],
    rename_all: Option<RenameCasing>,
) -> Result<(Vec<MappingProperty>, Vec<IterableProperties>), GeneratorError> {
    let mut properties = Vec::new();
    let mut content_properties = Vec::new();
    let mut content_iterable = Vec::new();

    // Add the tag field (discriminator)
    properties.push(MappingProperty {
        name: tag.to_string(),
        mapping: gen_string_mapping(),
    });

    // Process each variant's fields - they all go into the content field
    for variant in variants {
        match variant.fields.style {
            darling::ast::Style::Tuple => {
                // Tuples here are newtypes, use the fields of the type
                for field in &variant.fields.fields {
                    let ty = &field.ty;
                    content_iterable.push(IterableProperties {
                        source: quote::quote! {
                            <#ty as elastic_mapping::MappingType>::fields()
                        },
                    });
                }
            }
            darling::ast::Style::Struct => {
                // For struct variants, collect all fields
                for field in &variant.fields.fields {
                    content_properties.push(field.gen_mapping(rename_all));
                }
            }
            darling::ast::Style::Unit => {
                return Err(GeneratorError::Custom(
                    "Unit variants are not supported in tagged enums".to_string(),
                ));
            }
        }
    }

    // Generate the content field as an object containing all variant fields
    let content_prop_insertions: Vec<_> = content_properties
        .into_iter()
        .map(|prop| {
            let name = prop.name;
            let mapping = prop.mapping;
            quote::quote! {
                content_props.insert(#name.into(), #mapping);
            }
        })
        .collect();

    let content_iterable_insertions: Vec<_> = content_iterable
        .into_iter()
        .map(|it| {
            let source = it.source;
            quote::quote! {
                for (key, field) in #source {
                    content_props.insert(key, field);
                }
            }
        })
        .collect();

    let content_mapping = quote::quote! {
        {
            let mut m = elastic_mapping::serde_json::Map::default();
            m.insert(#TYPE_FIELD.into(), #OBJECT_TYPE.into());
            m.insert(#PROPERTIES_FIELD.into(), {
                let mut content_props = elastic_mapping::serde_json::Map::default();
                #(#content_prop_insertions)*
                #(#content_iterable_insertions)*
                content_props.into()
            });
            m.into()
        }
    };

    // Add the content field
    properties.push(MappingProperty {
        name: content.to_string(),
        mapping: content_mapping,
    });

    // Return properties (tag + content), no iterable at top level
    Ok((properties, vec![]))
}