derive_getters/
dissolve.rs1use 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 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}