1//! Attribute parsing for fields.
2
3use syn::{spanned::Spanned, Attribute, Meta, Result};
4
5use crate::{util::MetaListExt, DeriveWhere, Error, Skip, DERIVE_WHERE};
6#[cfg(feature = "zeroize")]
7use crate::{Trait, TraitImpl, ZeroizeFqs};
8
9/// Attributes on field.
10#[derive(Default)]
11#[cfg_attr(test, derive(Debug))]
12pub struct FieldAttr {
13 /// [`Trait`](crate::Trait)s to skip this field for.
14 #[cfg_attr(feature = "zeroize", allow(rustdoc::redundant_explicit_links))]
15 pub skip: Skip,
16 /// Use fully-qualified-syntax for the [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html) implementation on this field.
17 #[cfg(feature = "zeroize")]
18 pub zeroize_fqs: ZeroizeFqs,
19}
20
21impl FieldAttr {
22 /// Create [`FieldAttr`] from [`Attribute`]s.
23 pub fn from_attrs(
24 derive_wheres: &[DeriveWhere],
25 skip_inner: &Skip,
26 attrs: &[Attribute],
27 ) -> Result<Self> {
28 let mut self_ = FieldAttr::default();
29
30 for attr in attrs {
31 if attr.path().is_ident(DERIVE_WHERE) {
32 self_.add_meta(derive_wheres, skip_inner, &attr.meta)?
33 }
34 }
35
36 Ok(self_)
37 }
38
39 /// Add [`Meta`] to [`FieldAttr`].
40 fn add_meta(
41 &mut self,
42 derive_wheres: &[DeriveWhere],
43 skip_inner: &Skip,
44 meta: &Meta,
45 ) -> Result<()> {
46 debug_assert!(meta.path().is_ident(DERIVE_WHERE));
47
48 if let Meta::List(list) = meta {
49 let nested = list.parse_non_empty_nested_metas()?;
50
51 for meta in &nested {
52 if meta.path().is_ident(Skip::SKIP) {
53 self.skip
54 .add_attribute(derive_wheres, Some(skip_inner), meta)?;
55 continue;
56 }
57
58 #[cfg(feature = "zeroize")]
59 {
60 if meta.path().is_ident(Trait::Zeroize.as_str()) {
61 self.zeroize_fqs.add_attribute(meta, derive_wheres)?;
62 continue;
63 }
64 }
65
66 return Err(Error::option(meta.path().span()));
67 }
68
69 Ok(())
70 } else {
71 Err(Error::option_syntax(meta.span()))
72 }
73 }
74}
75