macro_input_core/fields.rs
1use crate::Def;
2#[cfg(feature = "legacy")]
3use macro_compose::{Collector, Lint};
4#[cfg(feature = "legacy")]
5use quote::ToTokens;
6use syn::Attribute;
7#[cfg(feature = "legacy")]
8use syn::{Error, Meta, NestedMeta, Path};
9
10/// `Defs` is a collection of [`Def`]s
11/// # Example
12/// ```
13/// # use macro_input_core as macro_input;
14/// use macro_input::{DefaultValue, Def, Defs};
15///
16/// const BAR_FIELD: Def = Def::new("foo", "bar", false, DefaultValue::Bool(None));
17/// const BAZ_FIELD: Def = Def::new("foo", "baz", false, DefaultValue::Str(None));
18/// const FOO_FIELDS: &[&Def] = &[&BAR_FIELD, &BAZ_FIELD];
19/// const FOO_FIELD_DEFS: Defs = Defs::new(FOO_FIELDS);
20/// ```
21pub struct Defs<'a> {
22 defs: &'a [&'a Def<'a>],
23}
24
25impl<'a> Defs<'a> {
26 /// create a new collection of [`Def`]s from a slice
27 #[must_use]
28 pub const fn new(defs: &'a [&'a Def<'a>]) -> Self {
29 Defs { defs }
30 }
31
32 /// return an empty collection of [`Def`]s
33 #[must_use]
34 pub const fn empty() -> &'static Defs<'static> {
35 const EMPTY: Defs<'static> = Defs { defs: &[] };
36 &EMPTY
37 }
38
39 /// strip away the attributes for all fields
40 ///
41 /// This is useful for attribute macros because rust has no way of knowing which attributes were used.
42 /// ```
43 /// # use macro_input_core as macro_input;
44 /// use macro_input::{DefaultValue, Def, Defs};
45 /// use syn::{parse_quote, Attribute};
46 ///
47 /// // construct some attributes
48 /// let attr1: Attribute = parse_quote!(#[foo(bar = false)]);
49 /// let attr2: Attribute = parse_quote!(#[foo(baz = "baz")]);
50 /// let attr3: Attribute = parse_quote!(#[foo(qux = "qux", quux = 3)]);
51 /// let attr4: Attribute = parse_quote!(#[foo(quuz = 'b')]);
52 /// let attr5: Attribute = parse_quote!(#[some(thing = "value")]);
53 /// let mut attrs = vec![attr1, attr2, attr3, attr4, attr5];
54 ///
55 /// // strip away all mentions of the fields bar, baz and qux in foo
56 /// const BAR_FIELD: Def = Def::new("foo", "bar", false, DefaultValue::Bool(None));
57 /// const BAZ_FIELD: Def = Def::new("foo", "baz", false, DefaultValue::Str(None));
58 /// const QUX_FIELD: Def = Def::new("foo", "qux", false, DefaultValue::Str(None));
59 /// const FOO_FIELDS: &[&Def] = &[&BAR_FIELD, &BAZ_FIELD, &QUX_FIELD];
60 /// const FOO_FIELD_DEFS: Defs = Defs::new(FOO_FIELDS);
61 /// FOO_FIELD_DEFS.strip(&mut attrs);
62 ///
63 /// // the Vec no longer contains
64 /// // #[foo(bar = false)], #[foo(baz = "baz")] and #[foo(qux = "qux")]
65 /// // but
66 /// // #[foo(quux = 3)], #[foo(quuz = 'b')], #[some(thing = "value")]
67 /// // remain
68 /// let attr1: Attribute = parse_quote!(#[foo(quux = 3)]);
69 /// let attr2: Attribute = parse_quote!(#[foo(quuz = 'b')]);
70 /// let attr3: Attribute = parse_quote!(#[some(thing = "value")]);
71 /// assert_eq!(attrs, vec![attr1, attr2, attr3]);
72 /// ```
73 pub fn strip(&self, attrs: &mut Vec<Attribute>) {
74 for def in self.defs {
75 def.strip(attrs);
76 }
77 }
78
79 #[cfg(feature = "legacy")]
80 fn has_path(&self, path: &Path) -> bool {
81 for def in self.defs.iter() {
82 if path.is_ident(def.path) {
83 return true;
84 }
85 }
86
87 false
88 }
89}
90
91impl<'a> From<&'a [&'a Def<'a>]> for Defs<'a> {
92 fn from(defs: &'a [&'a Def<'a>]) -> Self {
93 Defs::new(defs)
94 }
95}
96
97#[cfg(feature = "legacy")]
98impl Lint<Vec<Attribute>> for Defs<'_> {
99 fn lint(&self, input: &Vec<Attribute>, c: &mut Collector) {
100 for def in self.defs.iter() {
101 def.lint(input, c);
102 }
103
104 for attr in input.iter() {
105 let meta = attr.parse_meta().unwrap();
106 let path = meta.path();
107 if self.has_path(path) {
108 match &meta {
109 Meta::List(list) => {
110 for meta in list.nested.iter() {
111 match meta {
112 NestedMeta::Meta(meta) => {
113 match meta {
114 Meta::NameValue(_) | Meta::Path(_) => {}
115 _ => {
116 c.error(Error::new_spanned(
117 meta,
118 "expected name-and-value or path meta",
119 ));
120 }
121 }
122
123 let is_part_of_defs = |def: &&Def| {
124 path.is_ident(&def.path) && meta.path().is_ident(&def.name)
125 };
126 if !self.defs.iter().any(is_part_of_defs) {
127 c.error(Error::new_spanned(
128 meta,
129 format!(
130 "unrecognized attribute: {}::{}",
131 path.to_token_stream(),
132 meta.path().to_token_stream()
133 ),
134 ));
135 }
136 }
137 NestedMeta::Lit(l) => {
138 c.error(Error::new_spanned(l, "expected meta"));
139 }
140 }
141 }
142 }
143 _ => c.error(Error::new_spanned(meta, "expected a list meta")),
144 }
145 }
146 }
147 }
148}