use proc_macro2::Span;
use syn::{
DeriveInput, GenericParam, Generics, ImplGenerics, Path, Result, TypeGenerics, WhereClause,
};
#[cfg(not(feature = "nightly"))]
use crate::Discriminant;
#[cfg(feature = "zeroize")]
use crate::{
trait_::{zeroize::Zeroize, zeroize_on_drop::ZeroizeOnDrop},
DeriveTrait,
};
use crate::{Data, DeriveWhere, Either, Error, Item, ItemAttr, Trait};
pub struct Input<'a> {
pub crate_: Option<Path>,
pub derive_wheres: Vec<DeriveWhere>,
pub generics: SplitGenerics<'a>,
pub item: Item<'a>,
}
impl<'a> Input<'a> {
pub fn from_input(
span: Span,
DeriveInput {
attrs,
ident,
generics,
data,
..
}: &'a DeriveInput,
) -> Result<Self> {
let ItemAttr {
crate_,
skip_inner,
derive_wheres,
incomparable,
} = ItemAttr::from_attrs(span, data, attrs)?;
let mut found_incomparable = incomparable.0.is_some();
let item = match &data {
syn::Data::Struct(data) => Data::from_struct(
span,
&derive_wheres,
skip_inner,
incomparable,
ident,
&data.fields,
)
.map(Item::Item)?,
syn::Data::Enum(data) => {
#[cfg(not(feature = "nightly"))]
let discriminant = Discriminant::parse(attrs, &data.variants)?;
let variants = data
.variants
.iter()
.map(|variant| Data::from_variant(ident, &derive_wheres, variant))
.collect::<Result<Vec<Data>>>()?;
let mut found_default = false;
for variant in &variants {
if let Some(span) = variant.default_span() {
if found_default {
return Err(Error::default_duplicate(span));
} else {
found_default = true;
}
}
if let (Some(item), Some(variant)) = (incomparable.0, variant.incomparable.0) {
return Err(Error::incomparable_on_item_and_variant(item, variant));
}
found_incomparable |= variant.is_incomparable();
}
if !found_default
&& derive_wheres
.iter()
.any(|derive_where| derive_where.contains(Trait::Default))
{
return Err(Error::default_missing(span));
}
if !found_default
&& !found_incomparable
&& variants.iter().all(|variant| match variant.fields() {
Either::Left(fields) => fields.fields.is_empty(),
Either::Right(_) => true,
}) {
return Err(Error::item_empty(span));
}
Item::Enum {
#[cfg(not(feature = "nightly"))]
discriminant,
ident,
variants,
incomparable,
}
}
syn::Data::Union(data) => Data::from_union(
span,
&derive_wheres,
skip_inner,
incomparable,
ident,
&data.fields,
)
.map(Item::Item)?,
};
let generics_len = generics
.params
.iter()
.filter(|generic_param| match generic_param {
GenericParam::Type(_) => true,
GenericParam::Lifetime(_) | GenericParam::Const(_) => false,
})
.count();
'outer: for derive_where in &derive_wheres {
if derive_where.generics.len() != generics_len {
continue;
}
if derive_where.any_custom_bound() {
continue;
}
for generic_param in &generics.params {
if let GenericParam::Type(type_param) = generic_param {
if !derive_where.has_type_param(&type_param.ident) {
continue 'outer;
}
}
}
for (span, trait_) in derive_where.spans.iter().zip(&derive_where.traits) {
if trait_ == Trait::Default && item.is_enum() {
continue;
}
if item.any_skip_trait(***trait_) {
continue;
}
if found_incomparable {
continue;
}
#[cfg(feature = "zeroize")]
{
if let DeriveTrait::Zeroize(Zeroize { crate_: Some(_) })
| DeriveTrait::ZeroizeOnDrop(ZeroizeOnDrop {
crate_: Some(_), ..
})
| DeriveTrait::ZeroizeOnDrop(ZeroizeOnDrop { no_drop: true, .. }) = *trait_
{
continue;
}
if trait_ == Trait::Zeroize && item.any_fqs() {
continue;
}
}
return Err(Error::use_case(*span));
}
}
let generics = SplitGenerics::new(generics);
Ok(Self {
crate_,
derive_wheres,
generics,
item,
})
}
}
pub struct SplitGenerics<'a> {
pub imp: ImplGenerics<'a>,
pub ty: TypeGenerics<'a>,
pub where_clause: Option<&'a WhereClause>,
}
impl<'a> SplitGenerics<'a> {
fn new(generics: &'a Generics) -> Self {
let (imp, ty, where_clause) = generics.split_for_impl();
SplitGenerics {
imp,
ty,
where_clause,
}
}
}