derive_getters/
dissolve.rs
1use 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}