1use std::convert::TryFrom;
3
4use proc_macro2::{Span, TokenStream};
5use quote::{quote, ToTokens};
6use syn::parse::{Parse, ParseStream};
7use syn::{
8 Attribute, DataStruct, DeriveInput, Error, Fields, FieldsNamed, Ident, LitStr, Result, Type,
9};
10
11use crate::{extract::named_struct, faultmsg::Problem};
12
13#[derive(Debug, Clone, PartialEq, Eq)]
14enum Action {
15 Skip,
16 Rename(Ident),
17 Copy,
18}
19
20impl Parse for Action {
21 fn parse(input: ParseStream) -> Result<Self> {
22 syn::custom_keyword!(skip);
23 syn::custom_keyword!(rename);
24 syn::custom_keyword!(copy);
25
26 if input.peek(skip) {
27 let _ = input.parse::<skip>()?;
28 if !input.is_empty() {
29 Err(Error::new(Span::call_site(), Problem::TokensFollowSkip))
30 } else {
31 Ok(Action::Skip)
32 }
33 } else if input.peek(rename) {
34 let _ = input.parse::<rename>()?;
35 let _ = input.parse::<syn::Token![=]>()?;
36 let name = input.parse::<LitStr>()?;
37 if !input.is_empty() {
38 Err(Error::new(Span::call_site(), Problem::TokensFollowNewName))
39 } else {
40 Ok(Action::Rename(Ident::new(
41 name.value().as_str(),
42 Span::call_site(),
43 )))
44 }
45 } else if input.peek(copy) {
46 let _ = input.parse::<copy>()?;
47 if !input.is_empty() {
48 Err(Error::new(Span::call_site(), Problem::TokensFollowCopy))
49 } else {
50 Ok(Action::Copy)
51 }
52 } else {
53 Err(Error::new(Span::call_site(), Problem::InvalidAttribute))
54 }
55 }
56}
57
58#[derive(Debug, Clone)]
59struct Doc(TokenStream);
60
61#[derive(Debug, Clone)]
62struct Work {
63 special: Option<Action>,
65
66 docs: Vec<Doc>,
68}
69
70impl TryFrom<&[Attribute]> for Work {
71 type Error = Error;
72
73 fn try_from(attributes: &[Attribute]) -> Result<Self> {
74 let mut special: Option<Action> = None;
75 let mut docs: Vec<Doc> = Vec::new();
76
77 for attr in attributes {
78 if attr.path().is_ident("getter") {
79 special = Some(attr.parse_args::<Action>()?);
80 }
81
82 if attr.path().is_ident("doc") {
83 docs.push(Doc(attr.to_token_stream()));
84 }
85 }
86
87 Ok(Work { special, docs })
88 }
89}
90
91pub enum ReturnKind {
92 Copy,
93 Reference,
94}
95
96impl Default for ReturnKind {
97 fn default() -> Self {
98 ReturnKind::Reference
99 }
100}
101
102pub struct Field {
103 ty: Type,
104 name: Ident,
105 getter: Ident,
106 return_kind: ReturnKind,
107 docs: Vec<Doc>,
108}
109
110impl Field {
111 fn from_field(field: &syn::Field) -> Result<Option<Self>> {
112 let name: Ident = field
113 .ident
114 .clone()
115 .ok_or(Error::new(Span::call_site(), Problem::UnnamedField))?;
116
117 let work = Work::try_from(field.attrs.as_slice())?;
118
119 match work {
120 Work {
121 special: Some(Action::Skip),
122 ..
123 } => Ok(None),
124 Work { special, docs } => {
125 let ty = field.ty.clone();
126 let getter = match &special {
127 Some(Action::Rename(ident)) => ident.clone(),
128 _ => name.clone(),
129 };
130 let return_kind = match &special {
131 Some(Action::Copy) => ReturnKind::Copy,
132 _ => {
133 #[cfg(feature = "auto_copy_getters")]
134 if type_implements_copy(&ty) {
135 ReturnKind::Copy
136 } else {
137 ReturnKind::Reference
138 }
139 #[cfg(not(feature = "auto_copy_getters"))]
140 ReturnKind::Reference
141 }
142 };
143 Ok(Some(Field {
144 ty,
145 name,
146 getter,
147 return_kind,
148 docs,
149 }))
150 }
151 }
152 }
153
154 fn from_fields_named(fields_named: &FieldsNamed) -> Result<Vec<Self>> {
155 fields_named
156 .named
157 .iter()
158 .try_fold(Vec::new(), |mut fields, field| {
159 if let Some(field) = Field::from_field(field)? {
160 fields.push(field);
161 }
162
163 Ok(fields)
164 })
165 }
166
167 fn from_struct(structure: &DataStruct) -> Result<Vec<Self>> {
168 let fields_named = match structure.fields {
169 Fields::Named(ref fields) => Ok(fields),
170 Fields::Unnamed(_) => Err(Error::new(Span::call_site(), Problem::UnnamedField)),
171 Fields::Unit => Err(Error::new(Span::call_site(), Problem::UnitStruct)),
172 }?;
173
174 Self::from_fields_named(fields_named)
175 }
176
177 fn emit(&self, struct_name: &Ident) -> TokenStream {
178 let returns = &self.ty;
179 let field_name = &self.name;
180 let getter_name = &self.getter;
181
182 let doc_comments: Vec<TokenStream> = if self.docs.is_empty() {
183 let comment = format!(
184 " Get field `{}` from instance of `{}`.",
185 field_name, struct_name,
186 );
187
188 vec![quote!(#[doc=#comment])]
189 } else {
190 self.docs.iter().map(|d| d.0.to_owned()).collect()
191 };
192
193 match &self.ty {
194 Type::Reference(tr) => {
195 let lifetime = tr.lifetime.as_ref();
196 quote!(
197 #(#doc_comments)*
198 pub fn #getter_name(&#lifetime self) -> #returns {
199 self.#field_name
200 }
201 )
202 }
203 _ => match self.return_kind {
204 ReturnKind::Copy => quote!(
205 #(#doc_comments)*
206 pub fn #getter_name(&self) -> #returns {
207 self.#field_name
208 }
209 ),
210 ReturnKind::Reference => quote!(
211 #(#doc_comments)*
212 pub fn #getter_name(&self) -> &#returns {
213 &self.#field_name
214 }
215 ),
216 },
217 }
218 }
219}
220
221pub struct NamedStruct<'a> {
222 original: &'a DeriveInput,
223 name: Ident,
224 fields: Vec<Field>,
225}
226
227impl<'a> NamedStruct<'a> {
228 pub fn emit(&self) -> TokenStream {
229 let (impl_generics, struct_generics, where_clause) =
230 self.original.generics.split_for_impl();
231 let struct_name = &self.name;
232 let methods: Vec<TokenStream> = self
233 .fields
234 .iter()
235 .map(|field| field.emit(&self.name))
236 .collect();
237
238 let impl_comment = " Auto-generated by `derive_getters::Getters`.";
239 let impl_doc_comment = quote!(#[doc=#impl_comment]);
240
241 quote!(
242 #impl_doc_comment
243 impl #impl_generics #struct_name #struct_generics
244 #where_clause
245 {
246 #(#methods)*
247 }
248 )
249 }
250}
251
252impl<'a> TryFrom<&'a DeriveInput> for NamedStruct<'a> {
253 type Error = Error;
254
255 fn try_from(node: &'a DeriveInput) -> Result<Self> {
256 let struct_data = named_struct(node)?;
257 let fields = Field::from_struct(struct_data)?;
258
259 Ok(NamedStruct {
260 original: node,
261 name: node.ident.clone(),
262 fields,
263 })
264 }
265}
266
267#[cfg(test)]
268mod test {
269 use super::*;
270
271 #[test]
272 fn parse_action() -> Result<()> {
273 let a: Action = syn::parse_str("skip")?;
274 assert!(a == Action::Skip);
275
276 let r: Result<Action> = syn::parse_str("skip = blah");
277 assert!(r.is_err());
278
279 let a: Action = syn::parse_str("rename = \"hello\"")?;
280 let check = Action::Rename(Ident::new("hello", Span::call_site()));
281 assert!(a == check);
282
283 let r: Result<Action> = syn::parse_str("rename + \"chooga\"");
284 assert!(r.is_err());
285
286 let r: Result<Action> = syn::parse_str("rename = \"chooga\" | bongle");
287 assert!(r.is_err());
288
289 Ok(())
290 }
291}
292
293#[cfg(feature = "auto_copy_getters")]
294fn type_implements_copy(ty: &syn::Type) -> bool {
295 match ty {
296 Type::Array(array) => type_implements_copy(&array.elem), Type::BareFn(_) => true, Type::Group(group) => type_implements_copy(&group.elem),
299 Type::ImplTrait(_) => false, Type::Infer(_) => false, Type::Macro(_) => false, Type::Never(_) => true, Type::Paren(paren) => type_implements_copy(&paren.elem),
304 Type::Path(path) => type_path_implements_copy(path),
305 Type::Ptr(_) => false, Type::Reference(_) => false, Type::Slice(slice) => type_implements_copy(&slice.elem), Type::TraitObject(_) => false, Type::Tuple(tuple) => tuple.elems.iter().all(type_implements_copy), Type::Verbatim(_) => false, _ => false, }
313}
314
315#[cfg(feature = "auto_copy_getters")]
316fn type_path_implements_copy(path: &syn::TypePath) -> bool {
317 match path.to_token_stream().to_string().as_str() {
318 "u8" | "u16" | "u32" | "u64" | "u128" | "i8" | "i16" | "i32" | "i64" | "i128" | "f32"
319 | "f64" | "bool" | "char" | "usize" | "isize" => true,
320 _ => false,
321 }
322}