blob: 4e5890b4007b9ebecb2fa5fca0019a9a77d3db4b [file] [log] [blame]
//! Attribute parsing for variants.
use syn::{spanned::Spanned, Attribute, Fields, Meta, Result, Variant};
use crate::{util::MetaListExt, Default, DeriveWhere, Error, Incomparable, Skip, DERIVE_WHERE};
/// Attributes on variant.
#[derive(Default)]
pub struct VariantAttr {
/// Default variant.
pub default: Default,
/// [`Trait`](crate::Trait)s to skip all fields for.
pub skip_inner: Skip,
/// Comparing variant will yield `false` for [`PartialEq`] and [`None`] for
/// [`PartialOrd`].
pub incomparable: Incomparable,
}
impl VariantAttr {
/// Create [`VariantAttr`] from [`Attribute`]s.
pub fn from_attrs(
attrs: &[Attribute],
derive_wheres: &[DeriveWhere],
variant: &Variant,
) -> Result<Self> {
let mut self_ = VariantAttr::default();
for attr in attrs {
if attr.path().is_ident(DERIVE_WHERE) {
self_.add_meta(&attr.meta, derive_wheres, variant)?
}
}
Ok(self_)
}
/// Add [`Meta`] to [`VariantAttr`].
fn add_meta(
&mut self,
meta: &Meta,
derive_wheres: &[DeriveWhere],
variant: &Variant,
) -> Result<()> {
debug_assert!(meta.path().is_ident(DERIVE_WHERE));
if let Meta::List(list) = meta {
let nested = list.parse_non_empty_nested_metas()?;
if nested.is_empty() {
return Err(Error::empty(list.span()));
}
for meta in &nested {
if meta.path().is_ident(Skip::SKIP_INNER) {
// Don't allow `skip_inner` on empty variants.
match &variant.fields {
Fields::Named(fields) if fields.named.is_empty() => {
return Err(Error::option_skip_empty(variant.span()))
}
Fields::Unnamed(fields) if fields.unnamed.is_empty() => {
return Err(Error::option_skip_empty(variant.span()))
}
Fields::Unit => return Err(Error::option_skip_empty(variant.span())),
_ => self.skip_inner.add_attribute(derive_wheres, None, meta)?,
}
} else if meta.path().is_ident(Default::DEFAULT) {
self.default.add_attribute(meta, derive_wheres)?;
} else if meta.path().is_ident(Incomparable::INCOMPARABLE) {
self.incomparable.add_attribute(meta, derive_wheres)?;
} else {
return Err(Error::option(meta.path().span()));
}
}
Ok(())
} else {
Err(Error::option_syntax(meta.span()))
}
}
}