use syn::{
token::{Brace, Paren},
FieldPat, FieldsNamed, FieldsUnnamed, Ident, Pat, PatIdent, PatStruct, PatTupleStruct, Path,
Result, Token,
};
#[cfg(all(not(feature = "nightly"), feature = "safe"))]
use {std::iter, syn::punctuated::Punctuated, syn::PatRest};
use crate::{DeriveWhere, Field, Skip, Trait};
#[cfg_attr(test, derive(Debug))]
pub struct Fields<'a> {
pub self_pattern: Pat,
pub other_pattern: Pat,
#[cfg(all(not(feature = "nightly"), feature = "safe"))]
pub other_pattern_skip: Pat,
pub fields: Vec<Field<'a>>,
}
impl<'a> Fields<'a> {
pub fn from_named(
derive_wheres: &[DeriveWhere],
skip_inner: &Skip,
path: Path,
fields: &'a FieldsNamed,
) -> Result<Self> {
let fields = Field::from_named(derive_wheres, skip_inner, fields)?;
let self_pattern = Self::struct_pattern(path.clone(), &fields, |field| &field.self_ident);
#[cfg(all(not(feature = "nightly"), feature = "safe"))]
let other_pattern_skip = Pat::Struct(PatStruct {
attrs: Vec::new(),
qself: None,
path: path.clone(),
brace_token: Brace::default(),
fields: Punctuated::new(),
rest: Some(PatRest {
attrs: Vec::new(),
dot2_token: <Token![..]>::default(),
}),
});
let other_pattern = Self::struct_pattern(path, &fields, |field| &field.other_ident);
Ok(Self {
self_pattern,
other_pattern,
#[cfg(all(not(feature = "nightly"), feature = "safe"))]
other_pattern_skip,
fields,
})
}
pub fn from_unnamed(
derive_wheres: &[DeriveWhere],
skip_inner: &Skip,
path: Path,
fields: &'a FieldsUnnamed,
) -> Result<Self> {
let fields = Field::from_unnamed(derive_wheres, skip_inner, fields)?;
let self_pattern = Self::tuple_pattern(path.clone(), &fields, |field| &field.self_ident);
#[cfg(all(not(feature = "nightly"), feature = "safe"))]
let other_pattern_skip = Pat::TupleStruct(PatTupleStruct {
attrs: Vec::new(),
qself: None,
path: path.clone(),
paren_token: Paren::default(),
elems: iter::once(Pat::Rest(PatRest {
attrs: Vec::new(),
dot2_token: <Token![..]>::default(),
}))
.collect(),
});
let other_pattern = Self::tuple_pattern(path, &fields, |field| &field.other_ident);
Ok(Self {
self_pattern,
other_pattern,
#[cfg(all(not(feature = "nightly"), feature = "safe"))]
other_pattern_skip,
fields,
})
}
fn struct_pattern(
path: Path,
fields: &[Field],
field_ident: impl for<'b> Fn(&'b Field) -> &'b Ident,
) -> Pat {
Pat::Struct(PatStruct {
attrs: Vec::new(),
qself: None,
path,
brace_token: Brace::default(),
fields: fields
.iter()
.map(|field| FieldPat {
attrs: Vec::new(),
member: field.to_member(),
colon_token: Some(<Token![:]>::default()),
pat: Box::new(Pat::Ident(PatIdent {
attrs: Vec::new(),
by_ref: Some(<Token![ref]>::default()),
mutability: None,
ident: field_ident(field).clone(),
subpat: None,
})),
})
.collect(),
rest: None,
})
}
fn tuple_pattern(
path: Path,
fields: &[Field],
field_ident: impl for<'b> Fn(&'b Field) -> &'b Ident,
) -> Pat {
Pat::TupleStruct(PatTupleStruct {
attrs: Vec::new(),
qself: None,
path,
paren_token: Paren::default(),
elems: fields
.iter()
.map(|field| {
Pat::Ident(PatIdent {
attrs: Vec::new(),
by_ref: Some(<Token![ref]>::default()),
mutability: None,
ident: field_ident(field).clone(),
subpat: None,
})
})
.collect(),
})
}
#[cfg(feature = "zeroize")]
pub fn self_pattern_mut(&self) -> Pat {
let mut pattern = self.self_pattern.clone();
match &mut pattern {
Pat::Struct(pattern) => {
for field in &mut pattern.fields {
if let Pat::Ident(pattern) = &mut *field.pat {
pattern.mutability = Some(<Token![mut]>::default());
} else {
unreachable!("unexpected pattern")
}
}
}
Pat::TupleStruct(pattern) => {
for field in &mut pattern.elems {
if let Pat::Ident(pattern) = &mut *field {
pattern.mutability = Some(<Token![mut]>::default());
} else {
unreachable!("unexpected pattern")
}
}
}
_ => unreachable!("unexpected pattern"),
}
pattern
}
pub fn any_skip_trait(&self, trait_: Trait) -> bool {
self.fields.iter().any(|field| field.skip(trait_))
}
pub fn skip(&self, trait_: Trait) -> bool {
self.fields.iter().all(|field| field.skip(trait_))
}
}