macro_input_core/field.rs
1use crate::{convert::FromMeta, DefaultValue};
2#[cfg(feature = "legacy")]
3use macro_compose::{Collector, Context, Lint};
4use proc_macro2::Span;
5use quote::{format_ident, ToTokens};
6use std::{iter::FromIterator, mem::replace};
7use syn::{
8 parse::Parse, parse2, parse_quote, punctuated::Punctuated, Attribute, Error, Lit, Meta,
9 NestedMeta, Result,
10};
11
12/// a field definition
13/// # Example
14/// ```
15/// # use macro_input_core as macro_input;
16/// use macro_input::{DefaultValue, Def};
17///
18/// const BAR_FIELD: Def = Def::new("foo", "bar", false, DefaultValue::Bool(None));
19/// const BAZ_FIELD: Def = Def::new("foo", "baz", false, DefaultValue::Str(None));
20/// ```
21pub struct Def<'a> {
22 /// the path/namespace of the field
23 pub path: &'a str,
24 /// the name of the field
25 pub name: &'a str,
26 /// whether or not this field is required
27 pub required: bool,
28 /// the typed default value
29 pub default: DefaultValue,
30}
31
32impl<'a> Def<'a> {
33 /// create a new field definition
34 #[must_use]
35 pub const fn new(path: &'a str, name: &'a str, required: bool, default: DefaultValue) -> Self {
36 Def {
37 path,
38 name,
39 required,
40 default,
41 }
42 }
43
44 /// strip away the attributes for this field
45 ///
46 /// This is useful for attribute macros because rust has no way of knowing which attributes were used.
47 /// ```
48 /// # use macro_input_core as macro_input;
49 /// use macro_input::{DefaultValue, Def};
50 /// use syn::{parse_quote, Attribute};
51 ///
52 /// // construct some attributes
53 /// let foo_attr: Attribute = parse_quote!(#[foo(bar = false)]);
54 /// let other_attr: Attribute = parse_quote!(#[some(thing = "value")]);
55 /// let mut attrs = vec![foo_attr, other_attr.clone()];
56 ///
57 /// // strip away all mentions of the field bar in foo
58 /// const BAR_FIELD: Def = Def::new("foo", "bar", false, DefaultValue::Bool(None));
59 /// BAR_FIELD.strip(&mut attrs);
60 ///
61 /// // the Vec no longer contains #[foo(bar = false)] but #[some(thing = "value")] remains
62 /// assert_eq!(attrs, vec![other_attr]);
63 /// ```
64 pub fn strip(&self, attrs: &mut Vec<Attribute>) {
65 let data = replace(attrs, Vec::new());
66 attrs.extend(data.into_iter().filter_map(|mut a| {
67 if self.strip_from_attribute(&mut a) {
68 None
69 } else {
70 Some(a)
71 }
72 }));
73 }
74
75 /// strip the attribute and return whether it was empty
76 fn strip_from_attribute(&self, attr: &mut Attribute) -> bool {
77 let mut meta = if let Ok(meta) = attr.parse_meta() {
78 meta
79 } else {
80 return false;
81 };
82
83 // check the path
84 if !meta.path().is_ident(self.path) {
85 return false;
86 }
87
88 match &mut meta {
89 Meta::List(list) => {
90 let new_punctuated = list
91 .nested
92 .iter()
93 .filter(|meta| {
94 if let NestedMeta::Meta(meta) = meta {
95 !meta.path().is_ident(self.name)
96 } else {
97 true
98 }
99 })
100 .cloned();
101 list.nested = Punctuated::from_iter(new_punctuated);
102
103 let empty = list.nested.is_empty();
104 *attr = parse_quote!(#[#list]);
105 empty
106 }
107 Meta::Path(_) => true,
108 _ => false,
109 }
110 }
111
112 /// try to find the meta that has the value for this field
113 ///
114 /// # Errors
115 /// may return the error if the field is required but not found
116 pub fn get_meta(&self, attrs: &[Attribute]) -> Result<Option<Meta>> {
117 for attr in attrs.iter() {
118 let meta = attr.parse_meta()?;
119 if meta.path().is_ident(self.path) {
120 if let Meta::List(list) = meta {
121 for meta in list.nested.iter() {
122 if let NestedMeta::Meta(meta) = meta {
123 if meta.path().is_ident(self.name) {
124 return Ok(Some(meta.clone()));
125 }
126 }
127 }
128 }
129 }
130 }
131
132 if self.required {
133 return Err(Error::new(
134 Span::call_site(),
135 format!(
136 "attribute for required field not found: {}::{}",
137 self.path, self.name
138 ),
139 ));
140 }
141
142 // construct a default meta
143 if let Some(lit) = self.default.as_lit() {
144 let name = format_ident!("{}", self.path);
145
146 return Ok(Some(parse_quote!(#name = #lit)));
147 }
148
149 Ok(None)
150 }
151
152 /// try to find the literal that has the value for this field
153 ///
154 /// # Errors
155 /// may return the error if the field is required but not found
156 pub fn get_lit(&self, attrs: &[Attribute]) -> Result<Option<Lit>> {
157 Ok(self.get_meta(attrs)?.and_then(|m| match m {
158 Meta::NameValue(nvm) => Some(nvm.lit),
159 _ => None,
160 }))
161 }
162
163 /// try to parse the literal that has the value for this field
164 ///
165 /// # Errors
166 /// may return the error if parsing fails
167 pub fn get<L: Parse>(&self, attrs: &[Attribute]) -> Result<Option<L>> {
168 self.get_lit(attrs)?
169 .map(|lit| {
170 let tokens = lit.to_token_stream();
171 parse2(tokens)
172 })
173 .transpose()
174 }
175
176 /// try to extract the value from the literal that has the value for this field
177 ///
178 /// ```
179 /// # use macro_input_core as macro_input;
180 /// use macro_input::{DefaultValue, Def};
181 /// use syn::{parse_quote, Attribute};
182 ///
183 /// # fn main() -> syn::Result<()> {
184 /// let attr = parse_quote!(#[foo(bar = false)]);
185 /// const BAR_FIELD: Def = Def::new("foo", "bar", false, DefaultValue::Bool(None));
186 /// let value = BAR_FIELD.get_value::<bool>(&[attr])?;
187 /// assert_eq!(value, false);
188 /// # Ok(())
189 /// # }
190 /// ```
191 ///
192 /// # Errors
193 /// may return an error if the field doesn't exist or has a value of the wrong type
194 pub fn get_value<V: FromMeta>(&self, attrs: &[Attribute]) -> Result<V> {
195 self.get_meta(attrs).and_then(FromMeta::from)
196 }
197}
198
199#[cfg(feature = "legacy")]
200impl Lint<Vec<Attribute>> for Def<'_> {
201 fn lint(&self, input: &Vec<Attribute>, c: &mut Collector) {
202 let mut found = false;
203
204 for attr in input.iter() {
205 let meta = attr.parse_meta().unwrap();
206 if meta.path().is_ident(self.path) {
207 if let Meta::List(list) = meta {
208 for meta in list.nested.iter() {
209 if let NestedMeta::Meta(meta) = meta {
210 if meta.path().is_ident(self.name) {
211 match meta {
212 Meta::NameValue(meta) => {
213 if found {
214 c.error(Error::new_spanned(
215 meta,
216 format!("dupplicate {} attribute", self.path),
217 ));
218 } else {
219 found = true;
220 }
221
222 let some_lit = Some(&meta.lit);
223 let mut subcontext = Context::new_by_ref(c, &some_lit);
224 subcontext.lint(
225 &self
226 .default
227 .ty(!self.required
228 && !self.default.has_default_data()),
229 );
230 }
231 Meta::Path(_) => {
232 if found {
233 c.error(Error::new_spanned(
234 meta,
235 format!("dupplicate {} attribute", self.path),
236 ));
237 } else {
238 found = true;
239 }
240
241 let mut subcontext = Context::new_by_ref(c, &None);
242 subcontext.lint(
243 &self
244 .default
245 .ty(!self.required
246 && !self.default.has_default_data()),
247 );
248 }
249 _ => {
250 c.error(Error::new_spanned(&meta, "unexpected meta list"));
251 }
252 }
253 }
254 }
255 }
256 }
257 }
258 }
259
260 if !found && self.required {
261 c.error(Error::new(
262 Span::call_site(),
263 format!("missing required {} attribute", self.name),
264 ));
265 }
266 }
267}