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}