devise_core/
field.rs

1use std::ops::Deref;
2
3use quote::ToTokens;
4use proc_macro2::TokenStream;
5use syn::{self, Member, Index, punctuated::Punctuated, spanned::Spanned};
6
7use crate::derived::{Derived, Struct, Variant, Union};
8use crate::ItemInput;
9
10#[derive(Debug, Copy, Clone)]
11pub enum FieldParent<'p> {
12    Variant(Variant<'p>),
13    Struct(Struct<'p>),
14    Union(Union<'p>),
15}
16
17impl<'p> FieldParent<'p> {
18    pub fn input(&self) -> &ItemInput {
19        match self {
20            FieldParent::Variant(v) => v.parent.parent,
21            FieldParent::Struct(v) => v.parent,
22            FieldParent::Union(v) => v.parent,
23        }
24    }
25
26    pub fn attrs(&self) -> &[syn::Attribute] {
27        match self {
28            FieldParent::Variant(v) => &v.attrs,
29            FieldParent::Struct(_) | FieldParent::Union(_) => self.input().attrs(),
30        }
31    }
32}
33
34impl ToTokens for FieldParent<'_> {
35    fn to_tokens(&self, tokens: &mut TokenStream) {
36        match self {
37            FieldParent::Variant(v) => v.to_tokens(tokens),
38            FieldParent::Struct(v) => v.to_tokens(tokens),
39            FieldParent::Union(v) => v.to_tokens(tokens),
40        }
41    }
42}
43
44#[derive(Debug, Copy, Clone)]
45pub(crate) enum FieldsKind<'p> {
46    Named(&'p syn::FieldsNamed),
47    Unnamed(&'p syn::FieldsUnnamed),
48    Unit
49}
50
51impl<'a> From<&'a syn::Fields> for FieldsKind<'a> {
52    fn from(fields: &'a syn::Fields) -> Self {
53        match fields {
54            syn::Fields::Named(fs) => FieldsKind::Named(&fs),
55            syn::Fields::Unnamed(fs) => FieldsKind::Unnamed(&fs),
56            syn::Fields::Unit => FieldsKind::Unit,
57        }
58    }
59}
60
61#[derive(Debug, Copy, Clone)]
62pub struct Fields<'p> {
63    pub parent: FieldParent<'p>,
64    pub(crate) kind: FieldsKind<'p>,
65}
66
67impl<'p> From<Variant<'p>> for Fields<'p> {
68    fn from(v: Variant<'p>) -> Self {
69        Fields { parent: FieldParent::Variant(v), kind: (&v.inner.fields).into() }
70    }
71}
72
73impl<'p> From<Struct<'p>> for Fields<'p> {
74    fn from(v: Struct<'p>) -> Self {
75        Fields { parent: FieldParent::Struct(v), kind: (&v.inner.fields).into() }
76    }
77}
78
79impl<'p> From<Union<'p>> for Fields<'p> {
80    fn from(v: Union<'p>) -> Self {
81        Fields { parent: FieldParent::Union(v), kind: FieldsKind::Named(&v.inner.fields) }
82    }
83}
84
85impl<'f> Fields<'f> {
86    fn fields(&self) -> Option<&'f Punctuated<syn::Field, syn::token::Comma>> {
87        match self.kind {
88            FieldsKind::Named(i) => Some(&i.named),
89            FieldsKind::Unnamed(i) => Some(&i.unnamed),
90            FieldsKind::Unit => None
91        }
92    }
93
94    pub fn iter(self) -> impl Iterator<Item = Field<'f>> + Clone {
95        self.fields()
96            .into_iter()
97            .flat_map(|fields| fields.iter())
98            .enumerate()
99            .map(move |(index, field)| Field {
100                index,
101                field: Derived::from(field, self.parent),
102            })
103    }
104
105    pub fn is_empty(self) -> bool {
106        self.count() == 0
107    }
108
109    pub fn count(self) -> usize {
110        self.fields().map(|f| f.len()).unwrap_or(0)
111    }
112
113    pub fn are_named(self) -> bool {
114        match self.kind {
115            FieldsKind::Named(..) => true,
116            _ => false
117        }
118    }
119
120    pub fn are_unnamed(self) -> bool {
121        match self.kind {
122            FieldsKind::Unnamed(..) => true,
123            _ => false
124        }
125    }
126
127    pub fn are_unit(self) -> bool {
128        match self.kind {
129            FieldsKind::Unit => true,
130            _ => false
131        }
132    }
133
134    fn surround(self, tokens: TokenStream) -> TokenStream {
135        match self.kind {
136            FieldsKind::Named(..) => quote_spanned!(self.span() => { #tokens }),
137            FieldsKind::Unnamed(..) => quote_spanned!(self.span() => ( #tokens )),
138            FieldsKind::Unit => quote!()
139        }
140    }
141
142    pub fn match_tokens(self) -> TokenStream {
143        // This relies on match ergonomics to work in either case.
144        let idents = self.iter().map(|field| {
145            let match_ident = field.match_ident();
146            match field.ident {
147                Some(ref id) => quote!(#id: #match_ident),
148                None => quote!(#match_ident)
149            }
150
151        });
152
153        self.surround(quote!(#(#idents),*))
154    }
155
156    pub fn builder<F: Fn(Field) -> TokenStream>(&self, f: F) -> TokenStream {
157        match self.parent {
158            FieldParent::Struct(s) => s.builder(f),
159            FieldParent::Variant(v) => v.builder(f),
160            FieldParent::Union(_) => panic!("unions are not supported")
161        }
162    }
163}
164
165impl<'a> ToTokens for Fields<'a> {
166    fn to_tokens(&self, tokens: &mut TokenStream) {
167        match self.kind {
168            FieldsKind::Named(v) => v.to_tokens(tokens),
169            FieldsKind::Unnamed(v) => v.to_tokens(tokens),
170            FieldsKind::Unit => tokens.extend(quote_spanned!(self.parent.span() => ()))
171        }
172    }
173}
174
175#[derive(Debug, Copy, Clone)]
176pub struct Field<'f> {
177    pub field: Derived<'f, syn::Field, FieldParent<'f>>,
178    pub index: usize,
179}
180
181impl<'f> Field<'f> {
182    pub fn match_ident(self) -> syn::Ident {
183        let name = match self.ident {
184            Some(ref id) => format!("__{}", id),
185            None => format!("__{}", self.index)
186        };
187
188        syn::Ident::new(&name, self.span().into())
189    }
190
191    pub fn accessor(&self) -> TokenStream {
192        if let FieldParent::Variant(_) = self.parent {
193            let ident = self.match_ident();
194            quote!(#ident)
195        } else {
196            let span = self.field.span().into();
197            let member = match self.ident {
198                Some(ref ident) => Member::Named(ident.clone()),
199                None => Member::Unnamed(Index { index: self.index as u32, span })
200            };
201
202            quote_spanned!(span => self.#member)
203        }
204    }
205}
206
207impl<'f> Deref for Field<'f> {
208    type Target = Derived<'f, syn::Field, FieldParent<'f>>;
209
210    fn deref(&self) -> &Self::Target {
211        &self.field
212    }
213}
214
215impl<'f> ToTokens for Field<'f> {
216    fn to_tokens(&self, tokens: &mut TokenStream) {
217        self.field.to_tokens(tokens)
218    }
219}