syn_helpers/
fields.rs

1use std::{error::Error, iter};
2
3use either_n::Either3;
4use proc_macro2::{Ident, Span, TokenStream};
5use quote::{format_ident, quote, ToTokens};
6use syn::{parse_quote, Attribute, Expr, ExprUnary, Path, Token, Type};
7
8use crate::{HasAttributes, TypeOfSelf};
9
10/// Represents the fields in a structure. This could be the fields on a struct
11/// or the fields in the variant of a enum
12pub enum Fields {
13    /// `*name* { X: String }`
14    Named(Vec<NamedField>, Vec<Attribute>),
15    /// `*name*(String)`
16    Unnamed(Vec<UnnamedField>, Vec<Attribute>),
17    /// No fields `*name*`
18    Unit(Vec<Attribute>),
19}
20
21impl Fields {
22    pub fn get_field_attributes(&self) -> &[Attribute] {
23        match self {
24            Fields::Named(_, attributes)
25            | Fields::Unnamed(_, attributes)
26            | Fields::Unit(attributes) => attributes,
27        }
28    }
29
30    pub fn fields_iterator(&self) -> impl ExactSizeIterator<Item = NamedOrUnnamedField<'_>> {
31        match self {
32            Fields::Named(fields, ..) => {
33                Either3::One(fields.iter().map(NamedOrUnnamedField::Named))
34            }
35            Fields::Unnamed(fields, ..) => {
36                Either3::Two(fields.iter().map(NamedOrUnnamedField::Unnamed))
37            }
38            Fields::Unit(..) => Either3::Three(iter::empty()),
39        }
40    }
41
42    pub fn fields_iterator_mut(
43        &mut self,
44    ) -> impl ExactSizeIterator<Item = NamedOrUnnamedFieldMut<'_>> {
45        match self {
46            Fields::Named(fields, ..) => {
47                Either3::One(fields.iter_mut().map(NamedOrUnnamedFieldMut::Named))
48            }
49            Fields::Unnamed(fields, ..) => {
50                Either3::Two(fields.iter_mut().map(NamedOrUnnamedFieldMut::Unnamed))
51            }
52            Fields::Unit(..) => Either3::Three(iter::empty()),
53        }
54    }
55
56    pub fn to_pattern(&self, path: Path, type_of_self: TypeOfSelf) -> TokenStream {
57        Self::to_pattern_with_config(self, path, type_of_self, "")
58    }
59
60    pub fn to_pattern_with_config(
61        &self,
62        path: Path,
63        type_of_self: TypeOfSelf,
64        name_postfix: &'static str,
65    ) -> TokenStream {
66        match self {
67            Fields::Named(fields, ..) => {
68                let fields_pattern = fields
69                    .iter()
70                    .map(|field| field.get_pattern_with_config(type_of_self, name_postfix));
71                quote!(#path { #(#fields_pattern),* })
72            }
73            Fields::Unnamed(fields, ..) => {
74                let fields_pattern = fields
75                    .iter()
76                    .map(|field| field.get_pattern_with_config(type_of_self, name_postfix));
77                quote!(#path(#(#fields_pattern),*))
78            }
79            Fields::Unit(..) => quote!(#path),
80        }
81    }
82
83    pub fn to_constructor<'b>(
84        &'b self,
85        generator: impl Fn(NamedOrUnnamedField<'b>) -> Result<Expr, Box<dyn Error>>,
86        constructor: Path,
87    ) -> Result<Expr, Box<dyn Error>> {
88        match self {
89            Fields::Named(fields, ..) => {
90                let arguments = fields
91                    .iter()
92                    .map(|field| {
93                        generator(NamedOrUnnamedField::Named(field)).map(|value| {
94                            let field_name = field.name.clone();
95                            quote! { #field_name: #value }
96                        })
97                    })
98                    .collect::<Result<Vec<_>, _>>()?;
99                Ok(parse_quote! { #constructor { #(#arguments),* } })
100            }
101            Fields::Unnamed(fields, ..) => {
102                let arguments = fields
103                    .iter()
104                    .map(|field| generator(NamedOrUnnamedField::Unnamed(field)))
105                    .collect::<Result<Vec<_>, _>>()?;
106                Ok(parse_quote! { #constructor (#(#arguments),*) })
107            }
108            Fields::Unit(..) => Ok(parse_quote! { #constructor }),
109        }
110    }
111
112    /// Will just return `None` if member is not of matching form. Also will return `None` if cannot find the field
113    pub fn get_field_by_member(&self, member: syn::Member) -> Option<NamedOrUnnamedField> {
114        match self {
115            Fields::Named(named, _) => {
116                if let syn::Member::Named(ident) = member {
117                    named
118                        .iter()
119                        .find(|named| named.name == ident)
120                        .map(NamedOrUnnamedField::Named)
121                } else {
122                    None
123                }
124            }
125            Fields::Unnamed(unnamed, _) => {
126                if let syn::Member::Unnamed(idx) = member {
127                    unnamed
128                        .get(idx.index as usize)
129                        .map(NamedOrUnnamedField::Unnamed)
130                } else {
131                    None
132                }
133            }
134            Fields::Unit(_) => None,
135        }
136    }
137
138    /// Will just return `None` if member is not of matching form. Also will return `None` if cannot find the field
139    pub fn get_field_by_member_mut(
140        &mut self,
141        member: syn::Member,
142    ) -> Option<NamedOrUnnamedFieldMut> {
143        match self {
144            Fields::Named(named, _) => {
145                if let syn::Member::Named(ident) = member {
146                    named
147                        .iter_mut()
148                        .find(|named| named.name == ident)
149                        .map(NamedOrUnnamedFieldMut::Named)
150                } else {
151                    None
152                }
153            }
154            Fields::Unnamed(unnamed, _) => {
155                if let syn::Member::Unnamed(idx) = member {
156                    unnamed
157                        .get_mut(idx.index as usize)
158                        .map(NamedOrUnnamedFieldMut::Unnamed)
159                } else {
160                    None
161                }
162            }
163            Fields::Unit(_) => None,
164        }
165    }
166}
167
168/// Converts to syn::Fields to syn_helpers::Fields
169pub(crate) fn syn_fields_to_fields(fields: syn::Fields, attributes: Vec<syn::Attribute>) -> Fields {
170    match fields {
171        syn::Fields::Named(named_fields) => {
172            let named_fields = named_fields
173                .named
174                .into_iter()
175                .enumerate()
176                .map(|(idx, field)| NamedField {
177                    attrs: field.attrs,
178                    name: field.ident.unwrap(),
179                    ty: field.ty,
180                    is_used: false,
181                    used_for_trait: false,
182                    idx,
183                })
184                .collect::<Vec<_>>();
185
186            Fields::Named(named_fields, attributes)
187        }
188        syn::Fields::Unnamed(unnamed_fields) => {
189            let fields = unnamed_fields
190                .unnamed
191                .into_iter()
192                .enumerate()
193                .map(|(idx, field)| UnnamedField {
194                    idx,
195                    attrs: field.attrs,
196                    ty: field.ty,
197                    is_used: false,
198                    used_for_trait: false,
199                })
200                .collect::<Vec<_>>();
201
202            Fields::Unnamed(fields, attributes)
203        }
204        syn::Fields::Unit => Fields::Unit(attributes),
205    }
206}
207
208/// A *Field* is declaration of some data under a object
209pub trait Field: HasAttributes {
210    /// Returns true if this field matches the same type
211    #[cfg(feature = "field_is_type")]
212    fn is_type(&self, ty: &Type) -> bool {
213        self.get_type() == ty
214    }
215
216    fn get_type(&self) -> &Type;
217
218    /// Get a pattern for reading this field
219    fn get_pattern(&self, type_of_self: TypeOfSelf) -> TokenStream {
220        Self::get_pattern_with_config(self, type_of_self, "")
221    }
222
223    /// Get pattern with configuration for the ownership (with [TypeOfSelf] and whether to prefix the identifier)
224    fn get_pattern_with_config(
225        &self,
226        type_of_self: TypeOfSelf,
227        name_postfix: &'static str,
228    ) -> TokenStream;
229
230    /// Returns the fields type **if** it used for the trait (only used internally)
231    fn get_type_that_needs_constraint(&self) -> Option<Type>;
232}
233
234/// Getting a reference to the field is recorded
235pub trait FieldMut: Field {
236    /// Get a expression which refers to this field. Note that reference takes the form `_` + index of field, this is
237    /// to prevent possible clashes with parameter names.
238    /// Use [FieldMut::get_reference_with_config] to for different options
239    fn get_reference(&mut self) -> Expr {
240        Self::get_reference_with_config(self, true, "")
241    }
242
243    fn get_reference_with_config(
244        &mut self,
245        used_for_trait: bool,
246        name_postfix: &'static str,
247    ) -> Expr;
248}
249
250/// Either [NamedField] or [UnnamedField]
251pub enum NamedOrUnnamedField<'a> {
252    Named(&'a NamedField),
253    Unnamed(&'a UnnamedField),
254}
255
256/// Either [NamedField] or [UnnamedField]
257pub enum NamedOrUnnamedFieldMut<'a> {
258    Named(&'a mut NamedField),
259    Unnamed(&'a mut UnnamedField),
260}
261
262impl HasAttributes for NamedOrUnnamedField<'_> {
263    fn get_attributes(&self) -> &[Attribute] {
264        match self {
265            NamedOrUnnamedField::Named(named) => named.get_attributes(),
266            NamedOrUnnamedField::Unnamed(unnamed) => unnamed.get_attributes(),
267        }
268    }
269}
270
271impl Field for NamedOrUnnamedField<'_> {
272    fn get_type_that_needs_constraint(&self) -> Option<Type> {
273        match self {
274            NamedOrUnnamedField::Named(named) => named.get_type_that_needs_constraint(),
275            NamedOrUnnamedField::Unnamed(unnamed) => unnamed.get_type_that_needs_constraint(),
276        }
277    }
278
279    fn get_pattern_with_config(
280        &self,
281        type_of_self: TypeOfSelf,
282        name_postfix: &'static str,
283    ) -> TokenStream {
284        match self {
285            NamedOrUnnamedField::Named(named) => {
286                named.get_pattern_with_config(type_of_self, name_postfix)
287            }
288            NamedOrUnnamedField::Unnamed(unnamed) => {
289                unnamed.get_pattern_with_config(type_of_self, name_postfix)
290            }
291        }
292    }
293
294    fn get_type(&self) -> &Type {
295        match self {
296            NamedOrUnnamedField::Named(named) => named.get_type(),
297            NamedOrUnnamedField::Unnamed(unnamed) => unnamed.get_type(),
298        }
299    }
300}
301
302impl HasAttributes for NamedOrUnnamedFieldMut<'_> {
303    fn get_attributes(&self) -> &[Attribute] {
304        match self {
305            NamedOrUnnamedFieldMut::Named(named) => named.get_attributes(),
306            NamedOrUnnamedFieldMut::Unnamed(unnamed) => unnamed.get_attributes(),
307        }
308    }
309}
310
311impl Field for NamedOrUnnamedFieldMut<'_> {
312    fn get_type_that_needs_constraint(&self) -> Option<Type> {
313        match self {
314            NamedOrUnnamedFieldMut::Named(named) => named.get_type_that_needs_constraint(),
315            NamedOrUnnamedFieldMut::Unnamed(unnamed) => unnamed.get_type_that_needs_constraint(),
316        }
317    }
318
319    fn get_pattern_with_config(
320        &self,
321        type_of_self: TypeOfSelf,
322        name_postfix: &'static str,
323    ) -> TokenStream {
324        match self {
325            NamedOrUnnamedFieldMut::Named(named) => {
326                named.get_pattern_with_config(type_of_self, name_postfix)
327            }
328            NamedOrUnnamedFieldMut::Unnamed(unnamed) => {
329                unnamed.get_pattern_with_config(type_of_self, name_postfix)
330            }
331        }
332    }
333
334    fn get_type(&self) -> &Type {
335        match self {
336            NamedOrUnnamedFieldMut::Named(named) => named.get_type(),
337            NamedOrUnnamedFieldMut::Unnamed(unnamed) => unnamed.get_type(),
338        }
339    }
340}
341
342impl FieldMut for NamedOrUnnamedFieldMut<'_> {
343    fn get_reference_with_config(
344        &mut self,
345        used_for_trait: bool,
346        name_postfix: &'static str,
347    ) -> Expr {
348        match self {
349            NamedOrUnnamedFieldMut::Named(named) => {
350                named.get_reference_with_config(used_for_trait, name_postfix)
351            }
352            NamedOrUnnamedFieldMut::Unnamed(unnamed) => {
353                unnamed.get_reference_with_config(used_for_trait, name_postfix)
354            }
355        }
356    }
357}
358
359pub struct NamedField {
360    pub name: Ident,
361    pub attrs: Vec<Attribute>,
362    pub ty: Type,
363    idx: usize,
364    is_used: bool,
365    used_for_trait: bool,
366}
367
368impl HasAttributes for NamedField {
369    fn get_attributes(&self) -> &[Attribute] {
370        &self.attrs
371    }
372}
373
374impl Field for NamedField {
375    fn get_pattern_with_config(
376        &self,
377        type_of_self: TypeOfSelf,
378        name_postfix: &'static str,
379    ) -> TokenStream {
380        let Self {
381            name, is_used, idx, ..
382        } = self;
383        if *is_used {
384            let annotations = type_of_self.as_matcher_tokens();
385            let reference_name = format_ident!("_{}{}", idx, name_postfix);
386            quote!(#name: #annotations #reference_name)
387        } else {
388            quote!(#name: _)
389        }
390    }
391
392    fn get_type_that_needs_constraint(&self) -> Option<Type> {
393        self.used_for_trait.then(|| self.ty.clone())
394    }
395
396    fn get_type(&self) -> &Type {
397        &self.ty
398    }
399}
400
401impl FieldMut for NamedField {
402    fn get_reference_with_config(
403        &mut self,
404        used_for_trait: bool,
405        name_postfix: &'static str,
406    ) -> Expr {
407        self.is_used = true;
408        self.used_for_trait |= used_for_trait;
409        let path: Expr = syn::ExprPath {
410            attrs: Vec::new(),
411            qself: None,
412            path: format_ident!("_{}{}", self.idx, name_postfix).into(),
413        }
414        .into();
415        if matches!(self.ty, Type::Reference(..)) {
416            Expr::Unary(ExprUnary {
417                attrs: Vec::default(),
418                op: syn::UnOp::Deref(Token![*](Span::call_site())),
419                expr: Box::new(path),
420            })
421        } else {
422            path
423        }
424    }
425}
426
427pub struct UnnamedField {
428    pub idx: usize,
429    pub attrs: Vec<Attribute>,
430    pub ty: Type,
431    is_used: bool,
432    used_for_trait: bool,
433}
434
435impl HasAttributes for UnnamedField {
436    fn get_attributes(&self) -> &[Attribute] {
437        &self.attrs
438    }
439}
440
441impl Field for UnnamedField {
442    fn get_pattern_with_config(
443        &self,
444        type_of_self: TypeOfSelf,
445        name_postfix: &'static str,
446    ) -> TokenStream {
447        let Self { is_used, idx, .. } = self;
448        if *is_used {
449            let mut ts = type_of_self.as_matcher_tokens();
450            ts.extend(format_ident!("_{}{}", idx, name_postfix).to_token_stream());
451            ts
452        } else {
453            quote!(_)
454        }
455    }
456
457    fn get_type_that_needs_constraint(&self) -> Option<Type> {
458        self.used_for_trait.then(|| self.ty.clone())
459    }
460
461    fn get_type(&self) -> &Type {
462        &self.ty
463    }
464}
465
466impl FieldMut for UnnamedField {
467    fn get_reference_with_config(
468        &mut self,
469        used_for_trait: bool,
470        name_postfix: &'static str,
471    ) -> Expr {
472        self.is_used = true;
473        self.used_for_trait |= used_for_trait;
474        let path: Expr = syn::ExprPath {
475            attrs: Vec::new(),
476            qself: None,
477            path: format_ident!("_{}{}", self.idx, name_postfix).into(),
478        }
479        .into();
480        if matches!(self.ty, Type::Reference(..)) {
481            Expr::Unary(ExprUnary {
482                attrs: Vec::default(),
483                op: syn::UnOp::Deref(Token![*](Span::call_site())),
484                expr: Box::new(path),
485            })
486        } else {
487            path
488        }
489    }
490}