derive_getters/
getters.rs

1//! Getters internals
2use std::convert::TryFrom;
3
4use proc_macro2::{TokenStream, Span};
5use quote::{quote, ToTokens};
6use syn::{
7    DeriveInput,
8    FieldsNamed,
9    Type,
10    Ident,
11    LitStr,
12    Result,
13    Error,
14    Attribute,
15    parse::{Parse, ParseStream},
16};
17
18use crate::{
19    extract::{named_fields, named_struct},
20    faultmsg::Problem,
21};
22
23#[derive(Debug, Clone, PartialEq, Eq)]
24enum Action {    
25    Skip,
26    Rename(Ident),
27}
28
29impl Parse for Action {
30    fn parse(input: ParseStream) -> Result<Self> {
31        syn::custom_keyword!(skip);
32        syn::custom_keyword!(rename);
33        
34        if input.peek(skip) {
35            let _ = input.parse::<skip>()?;
36            if !input.is_empty() {
37                Err(Error::new(Span::call_site(), Problem::TokensFollowSkip))
38            } else {
39                Ok(Action::Skip)
40            }
41        } else if input.peek(rename) {
42            let _ = input.parse::<rename>()?;
43            let _ = input.parse::<syn::Token![=]>()?;
44            let name = input.parse::<LitStr>()?;
45            if !input.is_empty() {
46                Err(Error::new(Span::call_site(), Problem::TokensFollowNewName))
47            } else {
48                Ok(Action::Rename(Ident::new(name.value().as_str(), Span::call_site())))
49            }
50        } else {
51            Err(Error::new(Span::call_site(), Problem::InvalidAttribute))
52        }
53    }
54}
55
56#[derive(Debug, Clone)]
57struct Doc(TokenStream);
58
59#[derive(Debug, Clone)]
60struct Work {
61    // Whether to carry out an action (skip or rename) on the field.
62    special: Option<Action>,
63
64    // The documentation, if any, on the field.
65    docs: Vec<Doc>,
66}
67
68impl TryFrom<&[Attribute]> for Work {
69    type Error = Error;
70    
71    fn try_from(attributes: &[Attribute]) -> Result<Self> {
72        let mut special: Option<Action> = None;
73        let mut docs: Vec<Doc> = Vec::new();
74
75        for attr in attributes {
76            if attr.path.is_ident("getter") {
77                special = Some(attr.parse_args::<Action>()?);
78            }
79
80            if attr.path.is_ident("doc") {
81                docs.push(Doc(attr.to_token_stream()));
82            }
83        }
84
85        Ok(Work { special, docs })
86    }
87}
88
89pub struct Field {
90    ty: Type,    
91    name: Ident,
92    getter: Ident,
93    docs: Vec<Doc>,
94}
95
96impl Field {
97    fn from_field(field: &syn::Field) -> Result<Option<Self>> {
98        let name: Ident =  field.ident
99            .clone()
100            .ok_or(Error::new(Span::call_site(), Problem::UnnamedField))?;
101
102        let work = Work::try_from(field.attrs.as_slice())?;
103
104        match work {
105            Work { special: Some(Action::Skip), docs: _ } => Ok(None),
106            Work { special: Some(Action::Rename(ident)), docs } => {
107                Ok(Some(Field {
108                    ty: field.ty.clone(),
109                    name,
110                    getter: ident,
111                    docs
112                }))
113            },
114            Work { special: None, docs } => {
115                Ok(Some(Field {
116                    ty: field.ty.clone(),
117                    name: name.clone(),
118                    getter: name,
119                    docs,
120                }))
121            },
122        }
123    }
124    
125    fn from_fields_named(fields_named: &FieldsNamed) -> Result<Vec<Self>> {
126        fields_named.named
127            .iter()
128            .try_fold(Vec::new(), |mut fields, field| {
129                if let Some(field) = Field::from_field(field)? {
130                    fields.push(field);
131                }
132
133                Ok(fields)
134            })
135    }
136
137    fn emit(&self, struct_name: &Ident) -> TokenStream {
138        let returns = &self.ty;
139        let field_name = &self.name;
140        let getter_name = &self.getter;
141        
142        let doc_comments: Vec<TokenStream> = if self.docs.is_empty() {
143            let comment = format!(
144                " Get field `{}` from instance of `{}`.",
145                field_name,
146                struct_name,
147            );
148            
149            vec![quote!(#[doc=#comment])]
150        } else {
151            self.docs
152                .iter()
153                .map(|d| d.0.to_owned())
154                .collect()
155        };
156        
157        match &self.ty {
158            Type::Reference(tr) => {
159                let lifetime = tr.lifetime.as_ref();
160                quote!(
161                    #(#doc_comments)*
162                    pub fn #getter_name(&#lifetime self) -> #returns {
163                        self.#field_name
164                    }
165                )
166            },
167            _ => {
168                quote!(
169                    #(#doc_comments)*
170                    pub fn #getter_name(&self) -> &#returns {
171                        &self.#field_name
172                    }
173                )
174            },
175        }
176    }
177}
178
179pub struct NamedStruct<'a> {
180    original: &'a DeriveInput,
181    name: Ident,
182    fields: Vec<Field>,
183}
184
185impl<'a> NamedStruct<'a> {
186    pub fn emit(&self) -> TokenStream {
187        let (impl_generics, struct_generics, where_clause) = self.original.generics
188            .split_for_impl();        
189        let struct_name = &self.name;
190        let methods: Vec<TokenStream> = self.fields
191            .iter()
192            .map(|field| field.emit(&self.name))
193            .collect();
194
195        let impl_comment = " Auto-generated by `derive_getters::Getters`.";
196        let impl_doc_comment = quote!(#[doc=#impl_comment]);
197
198        quote!(
199            #impl_doc_comment
200            impl #impl_generics #struct_name #struct_generics
201                #where_clause
202            {
203                #(#methods)*
204            }
205        )        
206    }
207}
208
209impl<'a> TryFrom<&'a DeriveInput> for NamedStruct<'a> {
210    type Error = Error;
211    
212    fn try_from(node: &'a DeriveInput) -> Result<Self> {
213        let struct_data = named_struct(node)?;
214        let named_fields = named_fields(struct_data)?;
215        let fields = Field::from_fields_named(named_fields)?;
216
217        Ok(NamedStruct {
218            original: node,
219            name: node.ident.clone(),
220            fields,
221        })
222    }
223}
224
225#[cfg(test)]
226mod test {
227    use super::*;
228
229    #[test]
230    fn parse_action() -> Result<()> {
231        let a: Action = syn::parse_str("skip")?;
232        assert!(a == Action::Skip);
233
234        let r: Result<Action> = syn::parse_str("skip = blah");
235        assert!(r.is_err());
236
237        let a: Action = syn::parse_str("rename = \"hello\"")?;
238        let check = Action::Rename(Ident::new("hello", Span::call_site()));
239        assert!(a == check);
240
241        let r: Result<Action> = syn::parse_str("rename + \"chooga\"");        
242        assert!(r.is_err());
243
244        let r: Result<Action> = syn::parse_str("rename = \"chooga\" | bongle");
245        assert!(r.is_err());
246
247        Ok(())
248    }
249}