blob: 4b09c1ce7221f6681599ca46713a53ccdeecad87 [file] [log] [blame] [edit]
//! Types holding data of items.
mod field;
mod fields;
use proc_macro2::Span;
use syn::{Expr, 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};
/// Holds all relevant data of a struct, union or variant.
#[cfg_attr(test, derive(Debug))]
pub struct Data<'a> {
/// [`Skip`] attribute of this struct, union or variant.
skip_inner: Skip,
/// [`Incomparable`] attribute of this struct, union or variant.
pub incomparable: Incomparable,
/// [`struct@Ident`] of this struct, union or variant, used for implementing
/// [`Debug`](std::fmt::Debug).
pub ident: &'a Ident,
/// [`Path`] of this struct, union or variant, used to construct new
/// instances of that item, for example when implementing [`Clone`].
pub path: Path,
/// [Type](DataType) of this struct, union or variant.
pub type_: DataType<'a>,
/// Discriminant of this variant.
pub discriminant: Option<&'a Expr>,
}
/// Type of this data.
#[cfg_attr(test, derive(Debug))]
pub enum DataType<'a> {
/// Struct.
Struct(Fields<'a>),
/// Tuple.
Tuple(Fields<'a>),
/// Union.
Union(Fields<'a>),
/// Variant.
Variant {
/// [`struct@Default`] attribute of this variant.
default: Default,
/// [Type](VariantType) of this variant.
type_: VariantType<'a>,
},
/// Unit.
Unit(Pat),
}
/// Type of [`Data`].
#[cfg_attr(test, derive(Debug))]
pub enum VariantType<'a> {
/// Struct variant.
Struct(Fields<'a>),
/// Tuple variant.
Tuple(Fields<'a>),
/// Unit variant.
Unit(Pat),
}
/// Type to enable simplified matching.
pub enum SimpleType<'a> {
/// Struct, struct variant.
Struct(&'a Fields<'a>),
/// Tuple struct or tuple variant.
Tuple(&'a Fields<'a>),
/// Union.
Union(&'a Fields<'a>),
/// Unit variant.
Unit(&'a Pat),
}
impl<'a> Data<'a> {
/// Create [`Data`]s from [`syn::Fields`] of a struct.
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),
discriminant: None,
})
}
}
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),
discriminant: None,
})
}
}
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,
})),
discriminant: None,
}),
syn::Fields::Unit => Err(Error::item_empty(span)),
}
}
/// Create [`Data`]s from [`FieldsNamed`] of an union.
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),
discriminant: None,
})
}
}
/// Create [`Data`]s from [`syn::Fields`] of a variant.
pub fn from_variant(
item_ident: &'a Ident,
derive_wheres: &[DeriveWhere],
variant: &'a Variant,
) -> Result<Self> {
// Parse `Attribute`s on variant.
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),
},
discriminant: variant.discriminant.as_ref().map(|(_, expr)| expr),
})
}
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),
},
discriminant: variant.discriminant.as_ref().map(|(_, expr)| expr),
})
}
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),
},
discriminant: variant.discriminant.as_ref().map(|(_, expr)| expr),
})
}
}
}
/// Returns the [`Fields`] of this [`Data`]. If [`Data`] is a unit variant
/// or struct returns [`Pat`] instead.
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),
}
}
/// Returns the destructuring `self` pattern of this [`Data`].
pub fn self_pattern(&self) -> &Pat {
match self.fields() {
Either::Left(fields) => &fields.self_pattern,
Either::Right(pattern) => pattern,
}
}
/// Returns `true` if this variant is marked as the [`struct@Default`]. If
/// not a variant, always returns `true`.
pub fn is_default(&self) -> bool {
match self.type_ {
DataType::Variant { default, .. } => default.0.is_some(),
_ => true,
}
}
/// Returns `true` if this item or variant is marked as [`Incomparable`].
pub fn is_incomparable(&self) -> bool {
self.incomparable.0.is_some()
}
/// Returns [`Some`] if this variant has a [`struct@Default`]. If
/// not a variant, always returns [`None`].
pub fn default_span(&self) -> Option<Span> {
match &self.type_ {
DataType::Variant { default, .. } => default.0,
_ => None,
}
}
/// Returns `true` if this [`Data`] has no [`Fields`].
pub fn is_empty(&self, trait_: Trait) -> bool {
self.iter_fields(trait_).count() == 0
}
/// Returns `true` if a field is skipped with that [`Trait`].
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,
}
}
/// Returns `true` if all fields are skipped with that [`Trait`].
fn skip(&self, trait_: Trait) -> bool {
self.skip_inner.trait_skipped(trait_)
|| match self.fields() {
Either::Left(fields) => fields.skip(trait_),
Either::Right(_) => false,
}
}
/// Return a [`SimpleType`].
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),
}
}
/// Returns an [`Iterator`] over [`Field`]s.
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_))
}
/// Returns an [`Iterator`] over [`Member`]s.
pub fn iter_field_ident(&self, trait_: Trait) -> impl '_ + Iterator<Item = &'_ Member> {
self.iter_fields(trait_).map(|field| &field.member)
}
/// Returns an [`Iterator`] over [`struct@Ident`]s used as temporary
/// variables for destructuring `self`.
pub fn iter_self_ident(
&self,
trait_: Trait,
) -> impl Iterator<Item = &'_ Ident> + DoubleEndedIterator {
self.iter_fields(trait_).map(|field| &field.self_ident)
}
/// Returns an [`Iterator`] over [`struct@Ident`]s used as temporary
/// variables for destructuring `other`.
pub fn iter_other_ident(
&self,
trait_: Trait,
) -> impl Iterator<Item = &'_ Ident> + DoubleEndedIterator {
self.iter_fields(trait_).map(|field| &field.other_ident)
}
}