1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use crate::FieldDef;
#[cfg(feature = "legacy")]
use macro_compose::{Collector, Lint};
#[cfg(feature = "legacy")]
use quote::ToTokens;
use syn::Attribute;
#[cfg(feature = "legacy")]
use syn::{Error, Meta, NestedMeta, Path};

/// `FieldDefs` is a collection of [`FieldDef`]s
/// # Example
/// ```
/// # use macro_input_core as macro_input;
/// use macro_input::{DefaultValue, FieldDef, FieldDefs};
///
/// const BAR_FIELD: FieldDef = FieldDef::new("foo", "bar", false, DefaultValue::Bool(None));
/// const BAZ_FIELD: FieldDef = FieldDef::new("foo", "baz", false, DefaultValue::Str(None));
/// const FOO_FIELDS: &[&FieldDef] = &[&BAR_FIELD, &BAZ_FIELD];
/// const FOO_FIELD_DEFS: FieldDefs = FieldDefs::new(FOO_FIELDS);
/// ```
pub struct FieldDefs<'a> {
    defs: &'a [&'a FieldDef<'a>],
}

impl<'a> FieldDefs<'a> {
    /// create a new collection of [`FieldDef`]s from a slice
    pub const fn new(defs: &'a [&'a FieldDef<'a>]) -> Self {
        FieldDefs { defs }
    }

    /// return an empty collection of [`FieldDef`]s
    pub const fn empty() -> &'static FieldDefs<'static> {
        const EMPTY: FieldDefs<'static> = FieldDefs { defs: &[] };
        &EMPTY
    }

    /// strip the attributes for all field away
    pub fn strip(&self, attrs: &mut Vec<Attribute>) {
        for def in self.defs {
            def.strip(attrs);
        }
    }

    #[cfg(feature = "legacy")]
    fn has_path(&self, path: &Path) -> bool {
        for def in self.defs.iter() {
            if path.is_ident(def.path) {
                return true;
            }
        }

        false
    }
}

impl<'a> From<&'a [&'a FieldDef<'a>]> for FieldDefs<'a> {
    fn from(defs: &'a [&'a FieldDef<'a>]) -> Self {
        FieldDefs::new(defs)
    }
}

#[cfg(feature = "legacy")]
impl Lint<Vec<Attribute>> for FieldDefs<'_> {
    fn lint(&self, input: &Vec<Attribute>, c: &mut Collector) {
        for def in self.defs.iter() {
            def.lint(input, c);
        }

        for attr in input.iter() {
            let meta = attr.parse_meta().unwrap();
            let path = meta.path();
            if self.has_path(path) {
                match &meta {
                    Meta::List(list) => {
                        for meta in list.nested.iter() {
                            match meta {
                                NestedMeta::Meta(meta) => {
                                    match meta {
                                        Meta::NameValue(_) | Meta::Path(_) => {}
                                        _ => {
                                            c.error(Error::new_spanned(
                                                meta,
                                                "expected name-and-value or path meta",
                                            ));
                                        }
                                    }

                                    let is_part_of_defs = |def: &&FieldDef| {
                                        path.is_ident(&def.path) && meta.path().is_ident(&def.name)
                                    };
                                    if !self.defs.iter().any(is_part_of_defs) {
                                        c.error(Error::new_spanned(
                                            meta,
                                            format!(
                                                "unrecognized attribute: {}::{}",
                                                path.to_token_stream(),
                                                meta.path().to_token_stream()
                                            ),
                                        ));
                                    }
                                }
                                NestedMeta::Lit(l) => {
                                    c.error(Error::new_spanned(l, "expected meta"));
                                }
                            }
                        }
                    }
                    _ => c.error(Error::new_spanned(meta, "expected a list meta")),
                }
            }
        }
    }
}