roopert_macro_common/
accessors_attribute.rs

1use proc_macro2::{TokenStream};
2
3use syn::{ItemStruct, Field, Ident, Result, Token, punctuated::Punctuated, Type, Visibility, Expr, Path, Lit};
4use syn::parse::{Parse, ParseStream};
5
6use quote::{quote, ToTokens};
7
8use super::{Generate, RoopertAttribute, RoopertAttributeType, GetterAttribute, SetterAttribute};
9
10use super::parse::{is_getter_attribute, is_setter_attribute, is_roopert_attribute, single_path_segment};
11
12#[cfg_attr(feature="verbose", derive(Debug))]
13enum AccessorAutoRule {
14    All,
15    Private,
16    No,
17}
18
19#[derive(Clone)]
20struct FieldMetadata {
21    ty: Type,
22    ident: Ident,
23}
24
25impl FieldMetadata {
26    fn from_named_field(field: &Field) -> Self {
27        Self {
28            ty: field.ty.clone(),
29            ident: field.ident.clone().unwrap(),
30        }
31    }
32}
33
34impl AccessorAutoRule {
35    fn needs_accessor(&self, field: &Field) -> bool {
36        match self {
37            AccessorAutoRule::All => true,
38            AccessorAutoRule::Private => field.vis == Visibility::Inherited,
39            AccessorAutoRule::No => false,
40        }
41    }
42    
43    fn from_assignment_str(value: &str, input: ParseStream, ctx: &str) -> Result<AccessorAutoRule> {
44        match value {
45            "all" => Ok(AccessorAutoRule::All),
46            "private" => Ok(AccessorAutoRule::Private),
47            "no" => Ok(AccessorAutoRule::No),
48            _ => Err(input.error(format!("Unrecognised right hand side of assignment in #[roopert(accesssor, ..., {} = {}]", ctx, value)))
49        }
50    }
51}
52
53#[cfg_attr(feature="verbose", derive(Debug))]
54pub struct AccessorsAttribute {
55    getter_rule: AccessorAutoRule,
56    setter_rule: AccessorAutoRule,
57}
58
59impl AccessorsAttribute {
60    fn rule_from_expr(assignee: &Ident, expr: &Expr, input: ParseStream) -> Result<AccessorAutoRule> {
61        match expr {
62            Expr::Path(var) => 
63                AccessorAutoRule::from_assignment_str(&single_path_segment(&var.path, input, accessor_path_err_rule)?.to_string().to_lowercase(), input, &assignee.to_string()),
64            Expr::Lit(literal) => {
65                match &literal.lit {
66                    Lit::Str(lit_str) => 
67                        AccessorAutoRule::from_assignment_str(&lit_str.value().to_lowercase(), input, &assignee.to_string()),
68                    //Lit::Int(lit_int) => {},
69                    _ => Err(input.error(format!("Unsupported literal type in right hand side of assignment in #[roopert(accessor, ..., {} = ???]", assignee.to_string())))
70                }
71            },
72            _ => Err(input.error(format!("Unrecognised right hand side of assignment in #[roopert(accessor, ..., {} = ???)]", assignee.to_string())))
73        }
74    }
75}
76
77impl Parse for AccessorsAttribute {
78    fn parse(input: ParseStream) -> Result<Self> {
79        // parse for optional get and set rules
80        let mut get_rule = None;
81        let mut set_rule = None;
82        let params = Punctuated::<Expr, Token![,]>::parse_terminated(input)?;
83        for p in params.iter() {
84            match p {
85                Expr::Assign(assign) => {
86                    if let Expr::Path(var) = &*assign.left {
87                        let ident = single_path_segment(&var.path, input, accessor_path_err_left)?;
88                        match &ident.to_string().to_lowercase() as &str {
89                            "get" => {
90                                get_rule = Some(Self::rule_from_expr(&ident, &*assign.right, input)?);
91                                Ok(())
92                            },
93                            "set" => {
94                                set_rule = Some(Self::rule_from_expr(&ident, &*assign.right, input)?);
95                                Ok(())
96                            }
97                            _ => Err(input.error(format!("Unrecognised assignment {} in #[roopert(accessor, ...)]", ident.to_string())))
98                        }
99                    } else {
100                        Err(input.error("Unsupported left hand side of assignment in #[roopert(accessor, ..., ??? = ...]"))
101                    }
102                },
103                _ => Err(input.error(format!("Unrecognised attribute parameter {} in #[roopert(accessor, ...)]", p.to_token_stream())))
104            }?;
105        }
106        Ok(Self{
107            getter_rule: get_rule.unwrap_or(AccessorAutoRule::No),
108            setter_rule: set_rule.unwrap_or(AccessorAutoRule::No),
109        })
110    }
111}
112
113impl Generate for AccessorsAttribute {
114    fn generate(&mut self, input: TokenStream) -> core::result::Result<TokenStream, String> {
115        //self.attr.generate(input)
116        let mut target_struct: ItemStruct = syn::parse(input.into()).map_err(|_| "Only named structs objects can have roopert accessors".to_string())?;
117        let target_struct_ident = &target_struct.ident.clone();
118        let mut getters: Vec<(FieldMetadata, GetterAttribute)> = Vec::new();
119        let mut setters: Vec<(FieldMetadata, SetterAttribute)> = Vec::new();
120        
121        // find getter and setter attributes
122        for field in target_struct.fields.iter_mut() {
123            let mut setter_found = false;
124            let mut getter_found = false;
125            let field_meta = FieldMetadata::from_named_field(field);
126            
127            // get and set attributes must be removed after processing
128            // this stores any remaining attributes (which may be used by other macros or the compiler)
129            let mut new_attributes = Vec::with_capacity(field.attrs.len());
130            
131            // associate field type with field ident if has #[roopert(parent)] or #[parent] attr
132            for attr in &field.attrs {
133                let is_getter_path = is_getter_attribute(attr);
134                let is_setter_path = is_setter_attribute(attr);
135                let is_roopert_path = is_roopert_attribute(attr);
136                if is_getter_path {
137                    let getter = attr.parse_args::<GetterAttribute>().map_err(|_| "Malformed roopert #[get] attribute".to_string())?;
138                    getter_found = true;
139                    getters.push((field_meta.clone(), getter));
140                } else if is_setter_path {
141                    let setter = attr.parse_args::<SetterAttribute>().map_err(|_| "Malformed roopert #[set] attribute".to_string())?;
142                    setter_found = true;
143                    setters.push((field_meta.clone(), setter));
144                } else if is_roopert_path {
145                    let parsed_attr = attr.parse_args::<RoopertAttribute>().map_err(|e| format!("Malformed #[roopert(...)] attribute: {}", e))?;
146                    match parsed_attr.attr {
147                        RoopertAttributeType::Get(getter) => {
148                            getter_found = true;
149                            getters.push((
150                                field_meta.clone(), getter
151                            ))
152                        },
153                        RoopertAttributeType::Set(setter) => {
154                            setter_found = true;
155                            setters.push((
156                                field_meta.clone(), setter
157                            ))
158                        },
159                        _ => {
160                            new_attributes.push(attr.clone()); // keep non-related roopert attribute
161                            continue;
162                        }
163                    }
164                } else {
165                    new_attributes.push(attr.clone()); // keep unrelated attribute
166                }
167            }
168            field.attrs = new_attributes;
169            if !setter_found && self.setter_rule.needs_accessor(field) {
170                setters.push((field_meta.clone(), SetterAttribute::with_accessor_defaults()));
171            }
172            
173            if !getter_found && self.getter_rule.needs_accessor(field) {
174                getters.push((field_meta.clone(), GetterAttribute::with_accessor_defaults()));
175            }
176        }
177        
178        // generate accessors
179        let mut getter_tokens = Vec::new();
180        for (meta, attr) in getters {
181            getter_tokens.push(attr.impl_get_fn(&meta.ident, &meta.ty));
182        }
183        let mut setter_tokens = Vec::new();
184        for (meta, attr) in setters {
185            setter_tokens.push(attr.impl_set_fn(&meta.ident, &meta.ty));
186        }
187        let (impl_generics, ty_generics, where_clause) = target_struct.generics.split_for_impl();
188        Ok(quote!{
189            #target_struct
190            
191            impl #impl_generics #target_struct_ident #ty_generics #where_clause {
192                #(#getter_tokens)*
193                
194                #(#setter_tokens)*
195            }
196        })
197    }
198    
199    fn auto_append(&self) -> bool {false}
200}
201
202fn accessor_path_err_left(path: &Path) -> String {
203    format!("Unsupported path in left hand side of assignment in attribute #[roopert(accessors, ... = {})]", path.to_token_stream())
204}
205
206
207fn accessor_path_err_rule(path: &Path) -> String {
208    format!("Unsupported path in right hand side of assignment in attribute #[roopert(accessors, ... = {})]", path.to_token_stream())
209}