apollo-configuration-macros 0.1.1

Supporting macros for apollo-configuration (internal, do not use directly)
Documentation
use syn::spanned::Spanned as _;

use crate::ir::VariantDefinition;
use crate::ir::VariantFields;
use crate::parse::PartitionedAttrs;
use crate::parse::parse_named_field;
use crate::parse::partition_attrs;

/// The arguments to a `#[config]` variant attribute.
#[derive(Default)]
struct VariantArgs {
    /// Whether this variant is marked as the default.
    is_default: bool,
}

/// Parse a `#[config]` variant attribute.
fn parse_config_attribute(attr: &syn::Attribute) -> syn::Result<VariantArgs> {
    let mut args = VariantArgs::default();

    // Support plain `#[config]` w/o arguments
    if let syn::Meta::Path(_) = &attr.meta {
        return Ok(args);
    }

    attr.parse_nested_meta(|meta| {
        let ident = meta.path.require_ident()?;
        match ident.to_string().as_str() {
            "default" => {
                args.is_default = true;
            }
            _ => return Err(meta.error("unrecognized config attribute for variant")),
        }
        Ok(())
    })?;

    Ok(args)
}

pub(crate) fn parse_variant(input: syn::Variant) -> syn::Result<VariantDefinition> {
    let syn::Variant {
        attrs,
        ident,
        fields,
        discriminant: _,
    } = input;

    let PartitionedAttrs {
        config: config_attrs,
        cfg: cfg_attrs,
        other: attrs,
    } = partition_attrs(attrs);

    let args = match config_attrs.as_slice() {
        [] => Default::default(),
        [attr] => parse_config_attribute(attr)?,
        [_attr, extra, ..] => {
            return Err(syn::Error::new(
                extra.span(),
                "only one #[config] attribute is allowed per variant",
            ));
        }
    };

    // Parse fields based on variant type
    let parsed_fields = match fields {
        syn::Fields::Unit => VariantFields::Unit,
        syn::Fields::Unnamed(fields) => VariantFields::Tuple(fields),
        syn::Fields::Named(fields) => {
            let field_definitions = fields
                .named
                .into_iter()
                .map(parse_named_field)
                .collect::<Result<Vec<_>, _>>()?;
            VariantFields::Struct(field_definitions)
        }
    };

    Ok(VariantDefinition {
        name: ident,
        attrs,
        cfg_attrs,
        fields: parsed_fields,
        is_default: args.is_default,
    })
}