derive_getters/
getters.rs

1//! Getters internals
2use 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    // Whether to carry out an action (skip or rename) on the field.
64    special: Option<Action>,
65
66    // The documentation, if any, on the field.
67    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), // Assuming array.elem is the type of the elements and we recursively check if it implements Copy
297        Type::BareFn(_) => true,                                 // Function pointers implement Copy
298        Type::Group(group) => type_implements_copy(&group.elem),
299        Type::ImplTrait(_) => false, // ImplTrait does not implement Copy
300        Type::Infer(_) => false,     // Infer does not implement Copy
301        Type::Macro(_) => false,     // Macros do not implement Copy
302        Type::Never(_) => true,      // The Never type (!) implements Copy
303        Type::Paren(paren) => type_implements_copy(&paren.elem),
304        Type::Path(path) => type_path_implements_copy(path),
305        Type::Ptr(_) => false,       // Raw pointers do not implement Copy
306        Type::Reference(_) => false, // References do not implement Copy
307        Type::Slice(slice) => type_implements_copy(&slice.elem), // Similar to arrays
308        Type::TraitObject(_) => false, // Trait objects do not implement Copy
309        Type::Tuple(tuple) => tuple.elems.iter().all(type_implements_copy), // All elements in the tuple must implement Copy
310        Type::Verbatim(_) => false, // Verbatim is a catch-all and does not implement Copy
311        _ => false,                 // Catch all for any other types not explicitly matched
312    }
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}