derive_getters/
dissolve.rs

1//! Dissolve internals
2use std::{convert::TryFrom, iter::Extend};
3
4use proc_macro2::{Delimiter, Group, Span, TokenStream};
5use quote::quote;
6use syn::{
7    parse::{Parse, ParseStream},
8    punctuated::Punctuated,
9    token::Paren,
10    AttrStyle, Attribute, DataStruct, DeriveInput, Error, Fields, FieldsNamed, FieldsUnnamed,
11    Ident, Index, LitStr, Result, Type, TypeTuple,
12};
13
14use crate::{extract::named_struct, faultmsg::Problem};
15
16pub enum IndexOrName {
17    Index(Index),
18    Name(Ident),
19}
20
21pub struct Field {
22    ty: Type,
23    name: IndexOrName,
24}
25
26impl Field {
27    fn from_field(field: &syn::Field) -> Result<Self> {
28        let name: Ident = field
29            .ident
30            .clone()
31            .ok_or(Error::new(Span::call_site(), Problem::UnnamedField))?;
32
33        Ok(Field {
34            ty: field.ty.clone(),
35            name: IndexOrName::Name(name),
36        })
37    }
38
39    fn from_fields_named(fields_named: &FieldsNamed) -> Result<Vec<Self>> {
40        fields_named.named.iter().map(Field::from_field).collect()
41    }
42
43    fn from_fields_unnamed(fields_unnamed: &FieldsUnnamed) -> Result<Vec<Self>> {
44        fields_unnamed
45            .unnamed
46            .iter()
47            .enumerate()
48            .map(|(i, field)| {
49                Ok(Field {
50                    ty: field.ty.clone(),
51                    name: IndexOrName::Index(Index::from(i)),
52                })
53            })
54            .collect()
55    }
56
57    fn from_struct(structure: &DataStruct) -> Result<Vec<Self>> {
58        match structure.fields {
59            Fields::Named(ref fields) => Self::from_fields_named(fields),
60            Fields::Unnamed(ref fields) => Self::from_fields_unnamed(fields),
61            Fields::Unit => Err(Error::new(Span::call_site(), Problem::UnitStruct)),
62        }
63    }
64}
65
66struct Rename {
67    name: Ident,
68}
69
70impl Parse for Rename {
71    fn parse(input: ParseStream) -> Result<Self> {
72        syn::custom_keyword!(rename);
73
74        if input.peek(rename) {
75            let _ = input.parse::<rename>()?;
76            let _ = input.parse::<syn::Token![=]>()?;
77            let name = input.parse::<LitStr>()?;
78            if !input.is_empty() {
79                Err(Error::new(Span::call_site(), Problem::TokensFollowNewName))
80            } else {
81                let name = Ident::new(name.value().as_str(), Span::call_site());
82                Ok(Rename { name })
83            }
84        } else {
85            Err(Error::new(Span::call_site(), Problem::InvalidAttribute))
86        }
87    }
88}
89
90fn dissolve_rename_from(attributes: &[Attribute]) -> Result<Option<Ident>> {
91    let mut current: Option<Ident> = None;
92
93    for attr in attributes {
94        if attr.style != AttrStyle::Outer {
95            continue;
96        }
97
98        if attr.path().is_ident("dissolve") {
99            let rename = attr.parse_args::<Rename>()?;
100            current = Some(rename.name);
101        }
102    }
103
104    Ok(current)
105}
106
107pub struct NamedStruct<'a> {
108    original: &'a DeriveInput,
109    name: Ident,
110    fields: Vec<Field>,
111    dissolve_rename: Option<Ident>,
112}
113
114impl<'a> NamedStruct<'a> {
115    pub fn emit(&self) -> TokenStream {
116        let (impl_generics, struct_generics, where_clause) =
117            self.original.generics.split_for_impl();
118        let struct_name = &self.name;
119
120        let types: Punctuated<Type, syn::Token![,]> =
121            self.fields.iter().fold(Punctuated::new(), |mut p, field| {
122                p.push(field.ty.clone());
123                p
124            });
125
126        let types_len = types.len();
127
128        let return_type = if types_len > 1 {
129            let tup_group = Group::new(Delimiter::Parenthesis, quote!(#types));
130            let type_tuple = TypeTuple {
131                paren_token: Paren {
132                    span: tup_group.delim_span(),
133                },
134                elems: types,
135            };
136
137            quote!(#type_tuple)
138        } else {
139            if let Some(elem) = types.first() {
140                quote!(#elem)
141            } else {
142                quote!(())
143            }
144        };
145
146        let fields: TokenStream =
147            self.fields
148                .iter()
149                .enumerate()
150                .fold(TokenStream::new(), |mut ts, (count, field)| {
151                    if count > 0 {
152                        ts.extend(quote!(,))
153                    }
154
155                    let field_name = &field.name;
156                    let field_expr = match field_name {
157                        IndexOrName::Name(name) => {
158                            quote!(
159                                self.#name
160                            )
161                        }
162                        IndexOrName::Index(i) => {
163                            quote!(
164                                self.#i
165                            )
166                        }
167                    };
168
169                    ts.extend(field_expr);
170
171                    ts
172                });
173
174        let body = if types_len > 0 {
175            quote! { ( #fields ) }
176        } else {
177            // Don't output `()` to avoid a compiler warning on an empty struct
178            TokenStream::new()
179        };
180
181        let dissolve = Ident::new("dissolve", Span::call_site());
182        let fn_name = self.dissolve_rename.as_ref().unwrap_or(&dissolve);
183
184        let impl_comment = " Auto-generated by `derive_getters::Dissolve`.";
185        let impl_doc_comment = quote!(#[doc=#impl_comment]);
186
187        let fn_comment = format!(
188            " Dissolve `{}` into a tuple consisting of its fields in order of declaration.",
189            struct_name,
190        );
191        let fn_doc_comment = quote!(#[doc=#fn_comment]);
192
193        quote!(
194            #impl_doc_comment
195            impl #impl_generics #struct_name #struct_generics
196                #where_clause
197            {
198                #fn_doc_comment
199                pub fn #fn_name(self) -> #return_type {
200                    #body
201                }
202            }
203        )
204    }
205}
206
207impl<'a> TryFrom<&'a DeriveInput> for NamedStruct<'a> {
208    type Error = Error;
209
210    fn try_from(node: &'a DeriveInput) -> Result<Self> {
211        let struct_data = named_struct(node)?;
212        let fields = Field::from_struct(struct_data)?;
213        let rename = dissolve_rename_from(node.attrs.as_slice())?;
214
215        Ok(NamedStruct {
216            original: node,
217            name: node.ident.clone(),
218            fields,
219            dissolve_rename: rename,
220        })
221    }
222}