mod field;
mod fields;
use proc_macro2::Span;
use syn::{FieldsNamed, Ident, Pat, PatPath, Path, Result, Variant};
pub use self::{
field::{Field, Member},
fields::Fields,
};
use crate::{util, Default, DeriveWhere, Either, Error, Incomparable, Skip, Trait, VariantAttr};
#[cfg_attr(test, derive(Debug))]
pub struct Data<'a> {
skip_inner: Skip,
pub incomparable: Incomparable,
pub ident: &'a Ident,
pub path: Path,
pub type_: DataType<'a>,
}
#[cfg_attr(test, derive(Debug))]
pub enum DataType<'a> {
Struct(Fields<'a>),
Tuple(Fields<'a>),
Union(Fields<'a>),
Variant {
default: Default,
type_: VariantType<'a>,
},
Unit(Pat),
}
#[cfg_attr(test, derive(Debug))]
pub enum VariantType<'a> {
Struct(Fields<'a>),
Tuple(Fields<'a>),
Unit(Pat),
}
pub enum SimpleType<'a> {
Struct(&'a Fields<'a>),
Tuple(&'a Fields<'a>),
Union(&'a Fields<'a>),
Unit(&'a Pat),
}
impl<'a> Data<'a> {
pub fn from_struct(
span: Span,
derive_wheres: &[DeriveWhere],
skip_inner: Skip,
incomparable: Incomparable,
ident: &'a Ident,
fields: &'a syn::Fields,
) -> Result<Self> {
let path = util::path_from_idents(&[ident]);
match fields {
syn::Fields::Named(fields) => {
if fields.named.is_empty() && incomparable.0.is_none() {
Err(Error::item_empty(span))
} else {
let fields =
Fields::from_named(derive_wheres, &skip_inner, path.clone(), fields)?;
Ok(Self {
skip_inner,
incomparable,
ident,
path,
type_: DataType::Struct(fields),
})
}
}
syn::Fields::Unnamed(fields) => {
if fields.unnamed.is_empty() && incomparable.0.is_none() {
Err(Error::item_empty(span))
} else {
let fields =
Fields::from_unnamed(derive_wheres, &skip_inner, path.clone(), fields)?;
Ok(Self {
skip_inner,
incomparable,
ident,
path,
type_: DataType::Tuple(fields),
})
}
}
syn::Fields::Unit if incomparable.0.is_some() => Ok(Self {
skip_inner,
incomparable,
ident,
path: path.clone(),
type_: DataType::Unit(Pat::Path(PatPath {
attrs: Vec::new(),
qself: None,
path,
})),
}),
syn::Fields::Unit => Err(Error::item_empty(span)),
}
}
pub fn from_union(
span: Span,
derive_wheres: &[DeriveWhere],
skip_inner: Skip,
incomparable: Incomparable,
ident: &'a Ident,
fields: &'a FieldsNamed,
) -> Result<Self> {
if fields.named.is_empty() && incomparable.0.is_none() {
Err(Error::item_empty(span))
} else {
let path = util::path_from_idents(&[ident]);
let fields = Fields::from_named(derive_wheres, &skip_inner, path.clone(), fields)?;
Ok(Self {
skip_inner,
incomparable,
ident,
path,
type_: DataType::Union(fields),
})
}
}
pub fn from_variant(
item_ident: &'a Ident,
derive_wheres: &[DeriveWhere],
variant: &'a Variant,
) -> Result<Self> {
let VariantAttr {
default,
skip_inner,
incomparable,
} = VariantAttr::from_attrs(&variant.attrs, derive_wheres, variant)?;
let path = util::path_from_idents(&[item_ident, &variant.ident]);
match &variant.fields {
syn::Fields::Named(fields) => {
let fields = Fields::from_named(derive_wheres, &skip_inner, path.clone(), fields)?;
Ok(Self {
skip_inner,
incomparable,
ident: &variant.ident,
path,
type_: DataType::Variant {
default,
type_: VariantType::Struct(fields),
},
})
}
syn::Fields::Unnamed(fields) => {
let fields =
Fields::from_unnamed(derive_wheres, &skip_inner, path.clone(), fields)?;
Ok(Self {
skip_inner,
incomparable,
ident: &variant.ident,
path,
type_: DataType::Variant {
default,
type_: VariantType::Tuple(fields),
},
})
}
syn::Fields::Unit => {
let pattern = Pat::Path(PatPath {
attrs: Vec::new(),
qself: None,
path: path.clone(),
});
Ok(Self {
skip_inner,
incomparable,
ident: &variant.ident,
path,
type_: DataType::Variant {
default,
type_: VariantType::Unit(pattern),
},
})
}
}
}
pub fn fields(&self) -> Either<&Fields, &Pat> {
match &self.type_ {
DataType::Struct(fields)
| DataType::Tuple(fields)
| DataType::Union(fields)
| DataType::Variant {
type_: VariantType::Struct(fields),
..
}
| DataType::Variant {
type_: VariantType::Tuple(fields),
..
} => Either::Left(fields),
DataType::Unit(pattern)
| DataType::Variant {
type_: VariantType::Unit(pattern),
..
} => Either::Right(pattern),
}
}
pub fn self_pattern(&self) -> &Pat {
match self.fields() {
Either::Left(fields) => &fields.self_pattern,
Either::Right(pattern) => pattern,
}
}
#[cfg(all(not(feature = "nightly"), feature = "safe"))]
pub fn other_pattern_skip(&self) -> &Pat {
match self.fields() {
Either::Left(fields) => &fields.other_pattern_skip,
Either::Right(pattern) => pattern,
}
}
pub fn is_default(&self) -> bool {
match self.type_ {
DataType::Variant { default, .. } => default.0.is_some(),
_ => true,
}
}
pub fn is_incomparable(&self) -> bool {
self.incomparable.0.is_some()
}
pub fn default_span(&self) -> Option<Span> {
match &self.type_ {
DataType::Variant { default, .. } => default.0,
_ => None,
}
}
pub fn is_empty(&self, trait_: Trait) -> bool {
self.iter_fields(trait_).count() == 0
}
pub fn any_skip_trait(&self, trait_: Trait) -> bool {
self.skip_inner.trait_skipped(trait_)
|| match self.fields() {
Either::Left(fields) => fields.any_skip_trait(trait_),
Either::Right(_) => false,
}
}
fn skip(&self, trait_: Trait) -> bool {
self.skip_inner.trait_skipped(trait_)
|| match self.fields() {
Either::Left(fields) => fields.skip(trait_),
Either::Right(_) => false,
}
}
pub fn simple_type(&self) -> SimpleType {
match &self.type_ {
DataType::Struct(fields)
| DataType::Variant {
type_: VariantType::Struct(fields),
..
} => SimpleType::Struct(fields),
DataType::Tuple(fields)
| DataType::Variant {
type_: VariantType::Tuple(fields),
..
} => SimpleType::Tuple(fields),
DataType::Unit(pattern)
| DataType::Variant {
type_: VariantType::Unit(pattern),
..
} => SimpleType::Unit(pattern),
DataType::Union(fields) => SimpleType::Union(fields),
}
}
pub fn iter_fields(
&self,
trait_: Trait,
) -> impl '_ + Iterator<Item = &'_ Field> + DoubleEndedIterator {
if self.skip(trait_) {
[].iter()
} else {
match self.fields() {
Either::Left(fields) => fields.fields.iter(),
Either::Right(_) => [].iter(),
}
}
.filter(move |field| !field.skip(trait_))
}
pub fn iter_field_ident(&self, trait_: Trait) -> impl '_ + Iterator<Item = &'_ Member> {
self.iter_fields(trait_).map(|field| &field.member)
}
pub fn iter_self_ident(
&self,
trait_: Trait,
) -> impl Iterator<Item = &'_ Ident> + DoubleEndedIterator {
self.iter_fields(trait_).map(|field| &field.self_ident)
}
pub fn iter_other_ident(
&self,
trait_: Trait,
) -> impl Iterator<Item = &'_ Ident> + DoubleEndedIterator {
self.iter_fields(trait_).map(|field| &field.other_ident)
}
}