tier-derive 0.1.17

Derive macros for tier Rust configuration metadata
Documentation
use quote::format_ident;
use syn::{DataStruct, Fields, FieldsUnnamed};

use crate::attr::parse_tier_attrs;
use crate::field::single_unnamed_field;
use crate::serde_attrs::{
    SerdeContainerAttrs, SerdeFieldContext, ensure_struct_container_attrs, has_field_naming_attrs,
};
use crate::ty::metadata_target_type;

use super::field::expand_named_fields_metadata;

pub(super) fn expand_struct_metadata(
    data_struct: DataStruct,
    container_attrs: &SerdeContainerAttrs,
) -> syn::Result<Vec<proc_macro2::TokenStream>> {
    ensure_struct_container_attrs(container_attrs)?;

    match data_struct.fields {
        Fields::Named(fields) => expand_named_fields_metadata(
            fields,
            SerdeFieldContext::for_struct(container_attrs),
            &format_ident!("metadata"),
            None,
        ),
        Fields::Unnamed(fields) => {
            expand_newtype_struct_metadata(fields, &format_ident!("metadata"))
        }
        Fields::Unit => Ok(Vec::new()),
    }
}

fn expand_newtype_struct_metadata(
    fields: FieldsUnnamed,
    accumulator: &proc_macro2::Ident,
) -> syn::Result<Vec<proc_macro2::TokenStream>> {
    let field = single_unnamed_field(
        fields,
        "TierConfig only supports tuple structs with exactly one field",
    )?;
    if parse_tier_attrs(&field.attrs)?.has_any() || has_field_naming_attrs(&field.attrs)? {
        return Err(syn::Error::new_spanned(
            field,
            "tuple struct wrappers cannot use field-level tier or serde naming attributes",
        ));
    }

    let metadata_ty = metadata_target_type(&field.ty);
    Ok(vec![quote::quote! {
        #accumulator.extend(<#metadata_ty as ::tier::TierMetadata>::metadata());
    }])
}