educe/trait_handlers/debug/models/field_attribute.rs
1use quote::ToTokens;
2use syn::{Attribute, Lit, Meta, NestedMeta};
3
4use super::super::super::create_path_string_from_lit_str;
5use crate::{panic, Trait};
6
7#[derive(Debug, Clone)]
8pub enum FieldAttributeName {
9 Default,
10 Custom(String),
11}
12
13impl FieldAttributeName {
14 pub fn into_option_string(self) -> Option<String> {
15 match self {
16 FieldAttributeName::Default => None,
17 FieldAttributeName::Custom(s) => Some(s),
18 }
19 }
20}
21
22#[derive(Debug, Clone)]
23pub struct FieldAttribute {
24 pub name: FieldAttributeName,
25 pub ignore: bool,
26 pub format_method: Option<String>,
27 pub format_trait: Option<String>,
28}
29
30#[derive(Debug, Clone)]
31pub struct FieldAttributeBuilder {
32 pub name: FieldAttributeName,
33 pub enable_name: bool,
34 pub enable_ignore: bool,
35 pub enable_impl: bool,
36}
37
38impl FieldAttributeBuilder {
39 #[allow(clippy::wrong_self_convention)]
40 pub fn from_debug_meta(&self, meta: &Meta) -> FieldAttribute {
41 let mut name = self.name.clone();
42
43 let mut ignore = false;
44
45 let mut format_method = None;
46 let mut format_trait = None;
47
48 let correct_usage_for_debug_attribute = {
49 let mut usage = vec![];
50
51 if self.enable_name {
52 usage.push(stringify!(#[educe(Debug = "new_name")]));
53 usage.push(stringify!(#[educe(Debug("new_name"))]));
54 }
55
56 if self.enable_ignore {
57 usage.push(stringify!(#[educe(Debug = false)]));
58 usage.push(stringify!(#[educe(Debug(false))]));
59 }
60
61 usage
62 };
63
64 let correct_usage_for_name = {
65 let usage = vec![
66 stringify!(#[educe(Debug(name = "new_name"))]),
67 stringify!(#[educe(Debug(name("new_name")))]),
68 ];
69
70 usage
71 };
72
73 let correct_usage_for_ignore = {
74 let usage = vec![stringify!(#[educe(Debug(ignore))])];
75
76 usage
77 };
78
79 let correct_usage_for_impl = {
80 let usage = vec![
81 stringify!(#[educe(Debug(method = "path_to_method"))]),
82 stringify!(#[educe(Debug(trait = "path_to_trait"))]),
83 stringify!(#[educe(Debug(trait = "path_to_trait", method = "path_to_method_in_trait"))]),
84 stringify!(#[educe(Debug(method("path_to_method")))]),
85 stringify!(#[educe(Debug(trait("path_to_trait")))]),
86 stringify!(#[educe(Debug(trait("path_to_trait"), method("path_to_method_in_trait")))]),
87 ];
88
89 usage
90 };
91
92 match meta {
93 Meta::List(list) => {
94 let mut name_is_set = false;
95 let mut ignore_is_set = false;
96
97 for p in list.nested.iter() {
98 match p {
99 NestedMeta::Meta(meta) => {
100 let meta_name = meta.path().into_token_stream().to_string();
101
102 match meta_name.as_str() {
103 "name" | "rename" => {
104 if !self.enable_name {
105 panic::unknown_parameter("Debug", meta_name.as_str());
106 }
107
108 match meta {
109 Meta::List(list) => {
110 for p in list.nested.iter() {
111 match p {
112 NestedMeta::Lit(lit) => match lit {
113 Lit::Str(s) => {
114 if name_is_set {
115 panic::reset_parameter(
116 meta_name.as_str(),
117 );
118 }
119
120 name_is_set = true;
121
122 let s =
123 create_path_string_from_lit_str(s);
124
125 name = match s {
126 Some(s) => {
127 FieldAttributeName::Custom(s)
128 },
129 None => {
130 panic::disable_named_field_name(
131 )
132 },
133 };
134 },
135 Lit::Bool(s) => {
136 if name_is_set {
137 panic::reset_parameter(
138 meta_name.as_str(),
139 );
140 }
141
142 name_is_set = true;
143
144 if s.value {
145 name = FieldAttributeName::Default;
146 } else {
147 panic::disable_named_field_name();
148 }
149 },
150 _ => panic::parameter_incorrect_format(
151 meta_name.as_str(),
152 &correct_usage_for_name,
153 ),
154 },
155 _ => panic::parameter_incorrect_format(
156 meta_name.as_str(),
157 &correct_usage_for_name,
158 ),
159 }
160 }
161 },
162 Meta::NameValue(named_value) => {
163 let lit = &named_value.lit;
164
165 match lit {
166 Lit::Str(s) => {
167 if name_is_set {
168 panic::reset_parameter(meta_name.as_str());
169 }
170
171 name_is_set = true;
172
173 let s = create_path_string_from_lit_str(s);
174
175 name = match s {
176 Some(s) => FieldAttributeName::Custom(s),
177 None => panic::disable_named_field_name(),
178 };
179 },
180 Lit::Bool(s) => {
181 if name_is_set {
182 panic::reset_parameter(meta_name.as_str());
183 }
184
185 name_is_set = true;
186
187 if s.value {
188 name = FieldAttributeName::Default;
189 } else {
190 panic::disable_named_field_name();
191 }
192 },
193 _ => panic::parameter_incorrect_format(
194 meta_name.as_str(),
195 &correct_usage_for_name,
196 ),
197 }
198 },
199 _ => panic::parameter_incorrect_format(
200 meta_name.as_str(),
201 &correct_usage_for_name,
202 ),
203 }
204 },
205 "ignore" => {
206 if !self.enable_ignore {
207 panic::unknown_parameter("Debug", meta_name.as_str());
208 }
209
210 match meta {
211 Meta::Path(_) => {
212 if ignore_is_set {
213 panic::reset_parameter(meta_name.as_str());
214 }
215
216 ignore_is_set = true;
217
218 ignore = true;
219 },
220 _ => panic::parameter_incorrect_format(
221 meta_name.as_str(),
222 &correct_usage_for_ignore,
223 ),
224 }
225 },
226 "method" => {
227 if !self.enable_impl {
228 panic::unknown_parameter("Debug", meta_name.as_str());
229 }
230
231 match meta {
232 Meta::List(list) => {
233 for p in list.nested.iter() {
234 match p {
235 NestedMeta::Lit(Lit::Str(s)) => {
236 if format_method.is_some() {
237 panic::reset_parameter(
238 meta_name.as_str(),
239 );
240 }
241
242 let s = create_path_string_from_lit_str(s);
243
244 if let Some(s) = s {
245 format_method = Some(s);
246 } else {
247 panic::empty_parameter(
248 meta_name.as_str(),
249 );
250 }
251 },
252 _ => panic::parameter_incorrect_format(
253 meta_name.as_str(),
254 &correct_usage_for_impl,
255 ),
256 }
257 }
258 },
259 Meta::NameValue(named_value) => {
260 let lit = &named_value.lit;
261
262 match lit {
263 Lit::Str(s) => {
264 if format_method.is_some() {
265 panic::reset_parameter(meta_name.as_str());
266 }
267
268 let s = create_path_string_from_lit_str(s);
269
270 if let Some(s) = s {
271 format_method = Some(s);
272 } else {
273 panic::empty_parameter(meta_name.as_str());
274 }
275 },
276 _ => panic::parameter_incorrect_format(
277 meta_name.as_str(),
278 &correct_usage_for_impl,
279 ),
280 }
281 },
282 _ => panic::parameter_incorrect_format(
283 meta_name.as_str(),
284 &correct_usage_for_impl,
285 ),
286 }
287 },
288 "trait" => {
289 if !self.enable_impl {
290 panic::unknown_parameter("Debug", meta_name.as_str());
291 }
292
293 match meta {
294 Meta::List(list) => {
295 for p in list.nested.iter() {
296 match p {
297 NestedMeta::Lit(Lit::Str(s)) => {
298 if format_trait.is_some() {
299 panic::reset_parameter(
300 meta_name.as_str(),
301 );
302 }
303
304 let s = create_path_string_from_lit_str(s);
305
306 if let Some(s) = s {
307 format_trait = Some(s);
308 } else {
309 panic::empty_parameter(
310 meta_name.as_str(),
311 );
312 }
313 },
314 _ => panic::parameter_incorrect_format(
315 meta_name.as_str(),
316 &correct_usage_for_impl,
317 ),
318 }
319 }
320 },
321 Meta::NameValue(named_value) => {
322 let lit = &named_value.lit;
323
324 match lit {
325 Lit::Str(s) => {
326 if format_trait.is_some() {
327 panic::reset_parameter(meta_name.as_str());
328 }
329
330 let s = create_path_string_from_lit_str(s);
331
332 if let Some(s) = s {
333 format_trait = Some(s);
334 } else {
335 panic::empty_parameter(meta_name.as_str());
336 }
337 },
338 _ => panic::parameter_incorrect_format(
339 meta_name.as_str(),
340 &correct_usage_for_impl,
341 ),
342 }
343 },
344 _ => panic::parameter_incorrect_format(
345 meta_name.as_str(),
346 &correct_usage_for_impl,
347 ),
348 }
349 },
350 _ => panic::unknown_parameter("Debug", meta_name.as_str()),
351 }
352 },
353 NestedMeta::Lit(lit) => match lit {
354 Lit::Str(s) => {
355 if !self.enable_name {
356 panic::attribute_incorrect_format(
357 "Debug",
358 &correct_usage_for_debug_attribute,
359 )
360 }
361
362 if name_is_set {
363 panic::reset_parameter("name");
364 }
365
366 name_is_set = true;
367
368 let s = create_path_string_from_lit_str(s);
369
370 name = match s {
371 Some(s) => FieldAttributeName::Custom(s),
372 None => panic::disable_named_field_name(),
373 };
374 },
375 Lit::Bool(b) => {
376 if !self.enable_ignore {
377 panic::attribute_incorrect_format(
378 "Debug",
379 &correct_usage_for_debug_attribute,
380 )
381 }
382
383 if ignore_is_set {
384 panic::reset_parameter("ignore");
385 }
386
387 ignore_is_set = true;
388
389 ignore = !b.value;
390 },
391 _ => panic::attribute_incorrect_format(
392 "Debug",
393 &correct_usage_for_debug_attribute,
394 ),
395 },
396 }
397 }
398 },
399 Meta::NameValue(named_value) => {
400 let lit = &named_value.lit;
401
402 match lit {
403 Lit::Str(s) => {
404 if !self.enable_name {
405 panic::attribute_incorrect_format(
406 "Debug",
407 &correct_usage_for_debug_attribute,
408 )
409 }
410
411 let s = create_path_string_from_lit_str(s);
412
413 name = match s {
414 Some(s) => FieldAttributeName::Custom(s),
415 None => panic::disable_named_field_name(),
416 };
417 },
418 Lit::Bool(b) => {
419 if !self.enable_ignore {
420 panic::attribute_incorrect_format(
421 "Debug",
422 &correct_usage_for_debug_attribute,
423 )
424 }
425
426 ignore = !b.value;
427 },
428 _ => panic::attribute_incorrect_format(
429 "Debug",
430 &correct_usage_for_debug_attribute,
431 ),
432 }
433 },
434 _ => panic::attribute_incorrect_format("Debug", &correct_usage_for_debug_attribute),
435 }
436
437 if format_trait.is_some() && format_method.is_none() {
438 format_method = Some("fmt".to_string());
439 }
440
441 FieldAttribute {
442 name,
443 ignore,
444 format_method,
445 format_trait,
446 }
447 }
448
449 #[allow(clippy::wrong_self_convention)]
450 pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> FieldAttribute {
451 let mut result = None;
452
453 for attribute in attributes.iter() {
454 if attribute.path.is_ident("educe") {
455 let meta = attribute.parse_meta().unwrap();
456
457 match meta {
458 Meta::List(list) => {
459 for p in list.nested.iter() {
460 match p {
461 NestedMeta::Meta(meta) => {
462 let meta_name = meta.path().into_token_stream().to_string();
463
464 let t = Trait::from_str(meta_name);
465
466 if traits.binary_search(&t).is_err() {
467 panic::trait_not_used(t);
468 }
469
470 if t == Trait::Debug {
471 if result.is_some() {
472 panic::reuse_a_trait(t);
473 }
474
475 result = Some(self.from_debug_meta(meta));
476 }
477 },
478 _ => panic::educe_format_incorrect(),
479 }
480 }
481 },
482 _ => panic::educe_format_incorrect(),
483 }
484 }
485 }
486
487 result.unwrap_or(FieldAttribute {
488 name: self.name,
489 ignore: false,
490 format_method: None,
491 format_trait: None,
492 })
493 }
494}