| 1 | //! Field parsing. |
| 2 | |
| 3 | use std::fmt::{self, Display, Formatter}; |
| 4 | |
| 5 | use proc_macro2::{Span, TokenStream}; |
| 6 | use quote::{format_ident, IdentFragment, ToTokens}; |
| 7 | use syn::{ext::IdentExt, Attribute, FieldsNamed, FieldsUnnamed, Ident, Index, Result, Type}; |
| 8 | |
| 9 | use crate::{DeriveWhere, FieldAttr, Skip, Trait}; |
| 10 | |
| 11 | /// Struct, union, struct variant or tuple variant field. |
| 12 | #[cfg_attr (test, derive(Debug))] |
| 13 | pub struct Field<'a> { |
| 14 | /// Attributes. |
| 15 | pub attr: FieldAttr, |
| 16 | /// [`struct@Ident`] or [`Index`] for this field. |
| 17 | pub member: Member<'a>, |
| 18 | /// [`struct@Ident`] used as a Temporary variable for destructuring `self`. |
| 19 | pub self_ident: Ident, |
| 20 | /// [`struct@Ident`] used as a Temporary variable for destructuring `other`. |
| 21 | pub other_ident: Ident, |
| 22 | /// [`Type`] used for asserting traits on fields for [`Eq`]. |
| 23 | pub type_: &'a Type, |
| 24 | } |
| 25 | |
| 26 | /// Borrowed version of [`syn::Member`], to avoid unnecessary allocations. |
| 27 | #[cfg_attr (test, derive(Debug))] |
| 28 | pub enum Member<'a> { |
| 29 | /// Named field. |
| 30 | Named(&'a Ident), |
| 31 | /// Unnamed field. |
| 32 | Unnamed(Index), |
| 33 | } |
| 34 | |
| 35 | impl IdentFragment for Member<'_> { |
| 36 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { |
| 37 | Display::fmt(&self, f) |
| 38 | } |
| 39 | } |
| 40 | |
| 41 | impl ToTokens for Member<'_> { |
| 42 | fn to_tokens(&self, tokens: &mut TokenStream) { |
| 43 | match self { |
| 44 | Member::Named(ident: &&Ident) => ident.to_tokens(tokens), |
| 45 | Member::Unnamed(index: &Index) => index.to_tokens(tokens), |
| 46 | } |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | impl Display for Member<'_> { |
| 51 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
| 52 | match self { |
| 53 | Member::Named(ident: &&Ident) => write!(f, " {}" , ident.unraw()), |
| 54 | Member::Unnamed(index: &Index) => write!(f, " {}" , index.index), |
| 55 | } |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | impl<'a> Field<'a> { |
| 60 | /// Create [`Field`]s from [`syn::FieldsNamed`]. |
| 61 | pub fn from_named( |
| 62 | derive_wheres: &[DeriveWhere], |
| 63 | skip_inner: &Skip, |
| 64 | fields: &'a FieldsNamed, |
| 65 | ) -> Result<Vec<Self>> { |
| 66 | fields |
| 67 | .named |
| 68 | .iter() |
| 69 | .map(|field| { |
| 70 | Field::from_field( |
| 71 | derive_wheres, |
| 72 | skip_inner, |
| 73 | &field.attrs, |
| 74 | Member::Named(field.ident.as_ref().expect("unexpected unnamed field" )), |
| 75 | &field.ty, |
| 76 | ) |
| 77 | }) |
| 78 | .collect() |
| 79 | } |
| 80 | |
| 81 | /// Create [`Field`]s from [`syn::FieldsUnnamed`]. |
| 82 | pub fn from_unnamed( |
| 83 | derive_wheres: &[DeriveWhere], |
| 84 | skip_inner: &Skip, |
| 85 | fields: &'a FieldsUnnamed, |
| 86 | ) -> Result<Vec<Self>> { |
| 87 | (0_u32..) |
| 88 | .zip(&fields.unnamed) |
| 89 | .map(|(index, field)| { |
| 90 | Field::from_field( |
| 91 | derive_wheres, |
| 92 | skip_inner, |
| 93 | &field.attrs, |
| 94 | Member::Unnamed(Index { |
| 95 | index, |
| 96 | span: Span::call_site(), |
| 97 | }), |
| 98 | &field.ty, |
| 99 | ) |
| 100 | }) |
| 101 | .collect() |
| 102 | } |
| 103 | |
| 104 | /// Create [`Field`] from [`syn::Field`]. |
| 105 | fn from_field( |
| 106 | derive_wheres: &[DeriveWhere], |
| 107 | skip_inner: &Skip, |
| 108 | attrs: &[Attribute], |
| 109 | member: Member<'a>, |
| 110 | type_: &'a Type, |
| 111 | ) -> Result<Self> { |
| 112 | let attr = FieldAttr::from_attrs(derive_wheres, skip_inner, attrs)?; |
| 113 | let self_ident = format_ident!("__field_ {}" , member); |
| 114 | let other_ident = format_ident!("__other_field_ {}" , member); |
| 115 | |
| 116 | Ok(Self { |
| 117 | attr, |
| 118 | member, |
| 119 | self_ident, |
| 120 | other_ident, |
| 121 | type_, |
| 122 | }) |
| 123 | } |
| 124 | |
| 125 | /// Convert to [`syn::Member`]. |
| 126 | pub fn to_member(&self) -> syn::Member { |
| 127 | match self.member { |
| 128 | Member::Named(ident) => syn::Member::Named(ident.clone()), |
| 129 | Member::Unnamed(ref index) => syn::Member::Unnamed(index.clone()), |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | /// Returns `true` if this field is skipped with the given [`Trait`]. |
| 134 | pub fn skip(&self, trait_: Trait) -> bool { |
| 135 | self.attr.skip.trait_skipped(trait_) |
| 136 | } |
| 137 | } |
| 138 | |