1//! Storage for items or variants with data.
2
3use syn::{
4 token::{Brace, Paren},
5 FieldPat, FieldsNamed, FieldsUnnamed, Ident, Pat, PatIdent, PatStruct, PatTupleStruct, Path,
6 Result, Token,
7};
8
9use crate::{DeriveWhere, Field, Skip, Trait};
10
11/// Struct, union, struct variant or tuple variant fields.
12#[cfg_attr(test, derive(Debug))]
13pub struct Fields<'a> {
14 /// [Pattern](Pat) to use in a match arm to destructure `self`.
15 pub self_pattern: Pat,
16 /// [Pattern](Pat) to use in a match arm to destructure `other`.
17 pub other_pattern: Pat,
18 /// [`Field`]s of this struct, union or variant.
19 pub fields: Vec<Field<'a>>,
20}
21
22impl<'a> Fields<'a> {
23 /// Create [`Fields`]s from [`FieldsNamed`].
24 pub fn from_named(
25 derive_wheres: &[DeriveWhere],
26 skip_inner: &Skip,
27 path: Path,
28 fields: &'a FieldsNamed,
29 ) -> Result<Self> {
30 let fields = Field::from_named(derive_wheres, skip_inner, fields)?;
31
32 let self_pattern = Self::struct_pattern(path.clone(), &fields, |field| &field.self_ident);
33 let other_pattern = Self::struct_pattern(path, &fields, |field| &field.other_ident);
34
35 Ok(Self {
36 self_pattern,
37 other_pattern,
38 fields,
39 })
40 }
41
42 /// Create [`Fields`]s from [`FieldsUnnamed`].
43 pub fn from_unnamed(
44 derive_wheres: &[DeriveWhere],
45 skip_inner: &Skip,
46 path: Path,
47 fields: &'a FieldsUnnamed,
48 ) -> Result<Self> {
49 let fields = Field::from_unnamed(derive_wheres, skip_inner, fields)?;
50
51 let self_pattern = Self::tuple_pattern(path.clone(), &fields, |field| &field.self_ident);
52 let other_pattern = Self::tuple_pattern(path, &fields, |field| &field.other_ident);
53
54 Ok(Self {
55 self_pattern,
56 other_pattern,
57 fields,
58 })
59 }
60
61 /// Destructuring pattern in a match arm for this item or variant.
62 fn struct_pattern(
63 path: Path,
64 fields: &[Field],
65 field_ident: impl for<'b> Fn(&'b Field) -> &'b Ident,
66 ) -> Pat {
67 Pat::Struct(PatStruct {
68 attrs: Vec::new(),
69 qself: None,
70 path,
71 brace_token: Brace::default(),
72 fields: fields
73 .iter()
74 .map(|field| FieldPat {
75 attrs: Vec::new(),
76 member: field.to_member(),
77 colon_token: Some(<Token![:]>::default()),
78 pat: Box::new(Pat::Ident(PatIdent {
79 attrs: Vec::new(),
80 by_ref: Some(<Token![ref]>::default()),
81 mutability: None,
82 ident: field_ident(field).clone(),
83 subpat: None,
84 })),
85 })
86 .collect(),
87 rest: None,
88 })
89 }
90
91 /// Destructuring pattern in a match arm for this item or variant.
92 fn tuple_pattern(
93 path: Path,
94 fields: &[Field],
95 field_ident: impl for<'b> Fn(&'b Field) -> &'b Ident,
96 ) -> Pat {
97 Pat::TupleStruct(PatTupleStruct {
98 attrs: Vec::new(),
99 qself: None,
100 path,
101 paren_token: Paren::default(),
102 elems: fields
103 .iter()
104 .map(|field| {
105 Pat::Ident(PatIdent {
106 attrs: Vec::new(),
107 by_ref: Some(<Token![ref]>::default()),
108 mutability: None,
109 ident: field_ident(field).clone(),
110 subpat: None,
111 })
112 })
113 .collect(),
114 })
115 }
116
117 /// Returns a [Pattern](Pat) to use in a match arm to destructure `self` as
118 /// mutable.
119 #[cfg(feature = "zeroize")]
120 pub fn self_pattern_mut(&self) -> Pat {
121 let mut pattern = self.self_pattern.clone();
122
123 match &mut pattern {
124 Pat::Struct(pattern) => {
125 for field in &mut pattern.fields {
126 if let Pat::Ident(pattern) = &mut *field.pat {
127 pattern.mutability = Some(<Token![mut]>::default());
128 } else {
129 unreachable!("unexpected pattern")
130 }
131 }
132 }
133 Pat::TupleStruct(pattern) => {
134 for field in &mut pattern.elems {
135 if let Pat::Ident(pattern) = &mut *field {
136 pattern.mutability = Some(<Token![mut]>::default());
137 } else {
138 unreachable!("unexpected pattern")
139 }
140 }
141 }
142 _ => unreachable!("unexpected pattern"),
143 }
144
145 pattern
146 }
147
148 /// Returns `true` if any field is skipped with that [`Trait`].
149 pub fn any_skip_trait(&self, trait_: Trait) -> bool {
150 self.fields.iter().any(|field| field.skip(trait_))
151 }
152
153 /// Returns `true` if all fields are skipped with that [`Trait`].
154 pub fn skip(&self, trait_: Trait) -> bool {
155 self.fields.iter().all(|field| field.skip(trait_))
156 }
157}
158