enumcapsulate-macros 0.6.3

Procedural macros for 'enumcapsulate' crate
Documentation
use syn::{meta::ParseNestedMeta, parse::Parse, punctuated::Punctuated};

#[derive(Clone, Default, PartialEq, Eq, Debug)]
pub(crate) struct ExcludeConfig {
    idents: Vec<syn::Ident>,
}

impl ExcludeConfig {
    pub(crate) fn parse(
        &mut self,
        meta: &ParseNestedMeta,
        accept_empty: bool,
    ) -> Result<(), syn::Error> {
        let input = meta.input;

        let lookahead = input.lookahead1();
        if lookahead.peek(syn::token::Paren) {
            let content;
            syn::parenthesized!(content in input);

            let punctuated: Punctuated<syn::Ident, syn::Token![,]> =
                content.parse_terminated(syn::Ident::parse, syn::Token![,])?;

            let idents = Vec::from_iter(punctuated);

            if idents.is_empty() {
                return Err(meta.error("expected non-empty list"));
            }

            let () = validate_derive_names(idents.iter())?;

            self.idents.extend(idents);
        } else if !accept_empty {
            return Err(meta.error("expected list"));
        }

        Ok(())
    }

    pub(crate) fn is_wildcard(&self) -> bool {
        self.idents.is_empty()
    }

    #[allow(dead_code)]
    pub(crate) fn is_included(&self, name: &str) -> bool {
        !self.is_excluded(name)
    }

    pub(crate) fn is_excluded(&self, name: &str) -> bool {
        if self.is_wildcard() {
            true
        } else {
            self.idents.iter().any(|ident| ident == name)
        }
    }

    #[allow(dead_code)]
    pub(crate) fn iter(&self) -> impl Iterator<Item = &syn::Ident> {
        self.idents.iter()
    }

    #[allow(dead_code)]
    pub(crate) fn into_iter(self) -> impl Iterator<Item = syn::Ident> {
        self.idents.into_iter()
    }
}

fn validate_derive_names<'a>(
    idents: impl Iterator<Item = &'a syn::Ident>,
) -> Result<(), syn::Error> {
    use crate::macro_name::*;

    const DERIVE_NAMES: &[&str] = &[
        FROM,
        TRY_INTO,
        FROM_VARIANT,
        INTO_VARIANT,
        AS_VARIANT,
        AS_VARIANT_REF,
        AS_VARIANT_MUT,
        VARIANT_DOWNCAST,
    ];

    let mut error: Option<syn::Error> = None;

    let unrecognized = idents.filter(|&ident| !DERIVE_NAMES.iter().any(|name| ident == name));

    for ident in unrecognized {
        let ident_err = syn::Error::new_spanned(ident, "unrecognized identifier");
        if let Some(error) = error.as_mut() {
            error.combine(ident_err);
        } else {
            error = Some(ident_err)
        }
    }

    if let Some(err) = error {
        return Err(err);
    }

    Ok(())
}