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
//! Attribute parsing for fields.

use syn::{spanned::Spanned, Attribute, Meta, NestedMeta, Result};

use crate::{DeriveWhere, Error, Skip, DERIVE_WHERE};
#[cfg(feature = "zeroize")]
use crate::{Trait, TraitImpl, ZeroizeFqs};

/// Attributes on field.
#[derive(Default)]
#[cfg_attr(test, derive(Debug))]
pub struct FieldAttr {
	/// [`Trait`](crate::Trait)s to skip this field for.
	pub skip: Skip,
	/// Use fully-qualified-syntax for the [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html) implementation on this field.
	#[cfg(feature = "zeroize")]
	pub zeroize_fqs: ZeroizeFqs,
}

impl FieldAttr {
	/// Create [`FieldAttr`] from [`Attribute`]s.
	pub fn from_attrs(
		derive_wheres: &[DeriveWhere],
		skip_inner: &Skip,
		attrs: &[Attribute],
	) -> Result<Self> {
		let mut self_ = FieldAttr::default();

		for attr in attrs {
			if attr.path.is_ident(DERIVE_WHERE) {
				match attr.parse_meta() {
					Ok(meta) => self_.add_meta(derive_wheres, skip_inner, &meta)?,
					Err(error) => return Err(Error::attribute_syntax(attr.span(), error)),
				}
			}
		}

		Ok(self_)
	}

	/// Add [`Meta`] to [`FieldAttr`].
	fn add_meta(
		&mut self,
		derive_wheres: &[DeriveWhere],
		skip_inner: &Skip,
		meta: &Meta,
	) -> Result<()> {
		debug_assert!(meta.path().is_ident(DERIVE_WHERE));

		if let Meta::List(list) = meta {
			if list.nested.is_empty() {
				return Err(Error::empty(list.span()));
			}

			for nested_meta in &list.nested {
				match nested_meta {
					NestedMeta::Meta(meta) => {
						if meta.path().is_ident(Skip::SKIP) {
							self.skip
								.add_attribute(derive_wheres, Some(skip_inner), meta)?;
							continue;
						}

						#[cfg(feature = "zeroize")]
						{
							if meta.path().is_ident(Trait::Zeroize.as_str()) {
								self.zeroize_fqs.add_attribute(meta, derive_wheres)?;
								continue;
							}
						}

						return Err(Error::option(meta.path().span()));
					}
					_ => return Err(Error::option_syntax(nested_meta.span())),
				}
			}

			Ok(())
		} else {
			Err(Error::option_syntax(meta.span()))
		}
	}
}