derive_getters/
dissolve.rs

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