educe/trait_handlers/debug/models/
field_attribute.rs1use syn::{punctuated::Punctuated, Attribute, Ident, Meta, Path, Token};
2
3use crate::{
4 common::{
5 ident_bool::{
6 meta_2_bool_allow_path, meta_2_ident, meta_name_value_2_bool, meta_name_value_2_ident,
7 meta_name_value_2_ident_and_bool, IdentOrBool,
8 },
9 path::meta_2_path,
10 },
11 panic,
12 supported_traits::Trait,
13};
14
15#[derive(Debug, Clone)]
16pub(crate) enum FieldName {
17 Default,
18 Custom(Ident),
19}
20
21pub(crate) struct FieldAttribute {
22 pub(crate) name: FieldName,
23 pub(crate) ignore: bool,
24 pub(crate) method: Option<Path>,
25}
26
27pub(crate) struct FieldAttributeBuilder {
28 pub(crate) enable_name: bool,
29 pub(crate) enable_ignore: bool,
30 pub(crate) enable_method: bool,
31 pub(crate) name: FieldName,
32}
33
34impl FieldAttributeBuilder {
35 pub(crate) fn build_from_debug_meta(&self, meta: &Meta) -> syn::Result<FieldAttribute> {
36 debug_assert!(meta.path().is_ident("Debug"));
37
38 let mut name = self.name.clone();
39 let mut ignore = false;
40 let mut method = None;
41
42 let correct_usage_for_debug_attribute = {
43 let mut usage = vec![];
44
45 if self.enable_name {
46 usage.push(stringify!(#[educe(Debug = NewName)]));
47 usage.push(stringify!(#[educe(Debug(name(NewName)))]));
48 }
49
50 if self.enable_ignore {
51 usage.push(stringify!(#[educe(Debug = false)]));
52 usage.push(stringify!(#[educe(Debug(ignore))]));
53 }
54
55 if self.enable_method {
56 usage.push(stringify!(#[educe(Debug(method(path_to_method)))]));
57 }
58
59 usage
60 };
61
62 match meta {
63 Meta::Path(_) => {
64 return Err(panic::attribute_incorrect_format(
65 meta.path().get_ident().unwrap(),
66 &correct_usage_for_debug_attribute,
67 ));
68 },
69 Meta::NameValue(name_value) => {
70 if self.enable_name {
71 if self.enable_ignore {
72 match meta_name_value_2_ident_and_bool(name_value)? {
73 IdentOrBool::Ident(ident) => {
74 name = FieldName::Custom(ident);
75 },
76 IdentOrBool::Bool(b) => {
77 ignore = !b;
78 },
79 }
80 } else {
81 name = FieldName::Custom(meta_name_value_2_ident(name_value)?);
82 }
83 } else if self.enable_ignore {
84 ignore = !meta_name_value_2_bool(name_value)?;
85 } else {
86 return Err(panic::attribute_incorrect_format(
87 meta.path().get_ident().unwrap(),
88 &correct_usage_for_debug_attribute,
89 ));
90 }
91 },
92 Meta::List(list) => {
93 let result =
94 list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
95
96 let mut name_is_set = false;
97 let mut ignore_is_set = false;
98 let mut method_is_set = false;
99
100 let mut handler = |meta: Meta| -> syn::Result<bool> {
101 if let Some(ident) = meta.path().get_ident() {
102 match ident.to_string().as_str() {
103 "name" | "rename" => {
104 if !self.enable_name {
105 return Ok(false);
106 }
107
108 let v = meta_2_ident(&meta)?;
109
110 if name_is_set {
111 return Err(panic::parameter_reset(ident));
112 }
113
114 name_is_set = true;
115
116 name = FieldName::Custom(v);
117
118 return Ok(true);
119 },
120 "ignore" => {
121 if !self.enable_ignore {
122 return Ok(false);
123 }
124
125 let v = meta_2_bool_allow_path(&meta)?;
126
127 if ignore_is_set {
128 return Err(panic::parameter_reset(ident));
129 }
130
131 ignore_is_set = true;
132
133 ignore = v;
134
135 return Ok(true);
136 },
137 "method" => {
138 if !self.enable_method {
139 return Ok(false);
140 }
141
142 let v = meta_2_path(&meta)?;
143
144 if method_is_set {
145 return Err(panic::parameter_reset(ident));
146 }
147
148 method_is_set = true;
149
150 method = Some(v);
151
152 return Ok(true);
153 },
154 _ => (),
155 }
156 }
157
158 Ok(false)
159 };
160
161 for p in result {
162 if !handler(p)? {
163 return Err(panic::attribute_incorrect_format(
164 meta.path().get_ident().unwrap(),
165 &correct_usage_for_debug_attribute,
166 ));
167 }
168 }
169 },
170 }
171
172 Ok(FieldAttribute {
173 name,
174 ignore,
175 method,
176 })
177 }
178
179 pub(crate) fn build_from_attributes(
180 &self,
181 attributes: &[Attribute],
182 traits: &[Trait],
183 ) -> syn::Result<FieldAttribute> {
184 let mut output = None;
185
186 for attribute in attributes.iter() {
187 let path = attribute.path();
188
189 if path.is_ident("educe") {
190 if let Meta::List(list) = &attribute.meta {
191 let result =
192 list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
193
194 for meta in result {
195 let path = meta.path();
196
197 let t = match Trait::from_path(path) {
198 Some(t) => t,
199 None => return Err(panic::unsupported_trait(meta.path())),
200 };
201
202 if !traits.contains(&t) {
203 return Err(panic::trait_not_used(path.get_ident().unwrap()));
204 }
205
206 if t == Trait::Debug {
207 if output.is_some() {
208 return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
209 }
210
211 output = Some(self.build_from_debug_meta(&meta)?);
212 }
213 }
214 }
215 }
216 }
217
218 Ok(output.unwrap_or(FieldAttribute {
219 name: self.name.clone(),
220 ignore: false,
221 method: None,
222 }))
223 }
224}