educe/trait_handlers/deref/models/
type_attribute.rs

1use syn::{punctuated::Punctuated, Attribute, Meta, Token};
2
3use crate::{panic, Trait};
4
5pub(crate) struct TypeAttribute;
6
7#[derive(Debug)]
8pub(crate) struct TypeAttributeBuilder {
9    pub(crate) enable_flag: bool,
10}
11
12impl TypeAttributeBuilder {
13    pub(crate) fn build_from_deref_meta(&self, meta: &Meta) -> syn::Result<TypeAttribute> {
14        debug_assert!(meta.path().is_ident("Deref"));
15
16        let correct_usage_for_deref_attribute = {
17            let mut usage = vec![];
18
19            if self.enable_flag {
20                usage.push(stringify!(#[educe(Deref)]));
21            }
22
23            usage
24        };
25
26        match meta {
27            Meta::Path(_) => {
28                if !self.enable_flag {
29                    return Err(panic::attribute_incorrect_format(
30                        meta.path().get_ident().unwrap(),
31                        &correct_usage_for_deref_attribute,
32                    ));
33                }
34            },
35            Meta::NameValue(_) | Meta::List(_) => {
36                return Err(panic::attribute_incorrect_format(
37                    meta.path().get_ident().unwrap(),
38                    &correct_usage_for_deref_attribute,
39                ));
40            },
41        }
42
43        Ok(TypeAttribute)
44    }
45
46    pub(crate) fn build_from_attributes(
47        &self,
48        attributes: &[Attribute],
49        traits: &[Trait],
50    ) -> syn::Result<TypeAttribute> {
51        let mut output = None;
52
53        for attribute in attributes.iter() {
54            let path = attribute.path();
55
56            if path.is_ident("educe") {
57                if let Meta::List(list) = &attribute.meta {
58                    let result =
59                        list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
60
61                    for meta in result {
62                        let path = meta.path();
63
64                        let t = match Trait::from_path(path) {
65                            Some(t) => t,
66                            None => return Err(panic::unsupported_trait(meta.path())),
67                        };
68
69                        if !traits.contains(&t) {
70                            return Err(panic::trait_not_used(path.get_ident().unwrap()));
71                        }
72
73                        if t == Trait::Deref {
74                            if output.is_some() {
75                                return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
76                            }
77
78                            output = Some(self.build_from_deref_meta(&meta)?);
79                        }
80                    }
81                }
82            }
83        }
84
85        Ok(output.unwrap_or(TypeAttribute))
86    }
87}