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 _ => 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 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 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 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 let mut new_attributes = Vec::with_capacity(field.attrs.len());
130
131 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()); continue;
162 }
163 }
164 } else {
165 new_attributes.push(attr.clone()); }
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 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}