educe/trait_handlers/eq/models/
type_attribute.rs

1use syn::{punctuated::Punctuated, Attribute, Meta, Token};
2
3use crate::{common::bound::Bound, panic, Trait};
4
5pub(crate) struct TypeAttribute {
6    pub(crate) bound: Bound,
7}
8
9#[derive(Debug)]
10pub(crate) struct TypeAttributeBuilder {
11    pub(crate) enable_flag:  bool,
12    pub(crate) enable_bound: bool,
13}
14
15impl TypeAttributeBuilder {
16    pub(crate) fn build_from_eq_meta(&self, meta: &Meta) -> syn::Result<TypeAttribute> {
17        debug_assert!(meta.path().is_ident("Eq"));
18
19        let mut bound = Bound::Auto;
20
21        let correct_usage_for_copy_attribute = {
22            let mut usage = vec![];
23
24            if self.enable_flag {
25                usage.push(stringify!(#[educe(Eq)]));
26            }
27
28            if self.enable_bound {
29                usage.push(stringify!(#[educe(Eq(bound(where_predicates)))]));
30                usage.push(stringify!(#[educe(Eq(bound = false))]));
31            }
32
33            usage
34        };
35
36        match meta {
37            Meta::Path(_) => {
38                if !self.enable_flag {
39                    return Err(panic::attribute_incorrect_format(
40                        meta.path().get_ident().unwrap(),
41                        &correct_usage_for_copy_attribute,
42                    ));
43                }
44            },
45            Meta::NameValue(_) => {
46                return Err(panic::attribute_incorrect_format(
47                    meta.path().get_ident().unwrap(),
48                    &correct_usage_for_copy_attribute,
49                ));
50            },
51            Meta::List(list) => {
52                let result =
53                    list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
54
55                let mut bound_is_set = false;
56
57                let mut handler = |meta: Meta| -> syn::Result<bool> {
58                    if let Some(ident) = meta.path().get_ident() {
59                        if ident == "bound" {
60                            if !self.enable_bound {
61                                return Ok(false);
62                            }
63
64                            let v = Bound::from_meta(&meta)?;
65
66                            if bound_is_set {
67                                return Err(panic::parameter_reset(ident));
68                            }
69
70                            bound_is_set = true;
71
72                            bound = v;
73
74                            return Ok(true);
75                        }
76                    }
77
78                    Ok(false)
79                };
80
81                for p in result {
82                    if !handler(p)? {
83                        return Err(panic::attribute_incorrect_format(
84                            meta.path().get_ident().unwrap(),
85                            &correct_usage_for_copy_attribute,
86                        ));
87                    }
88                }
89            },
90        }
91
92        Ok(TypeAttribute {
93            bound,
94        })
95    }
96
97    pub(crate) fn build_from_attributes(
98        &self,
99        attributes: &[Attribute],
100        traits: &[Trait],
101    ) -> syn::Result<TypeAttribute> {
102        let mut output = None;
103
104        for attribute in attributes.iter() {
105            let path = attribute.path();
106
107            if path.is_ident("educe") {
108                if let Meta::List(list) = &attribute.meta {
109                    let result =
110                        list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
111
112                    for meta in result {
113                        let path = meta.path();
114
115                        let t = match Trait::from_path(path) {
116                            Some(t) => t,
117                            None => return Err(panic::unsupported_trait(meta.path())),
118                        };
119
120                        if !traits.contains(&t) {
121                            return Err(panic::trait_not_used(path.get_ident().unwrap()));
122                        }
123
124                        if t == Trait::Eq {
125                            if output.is_some() {
126                                return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
127                            }
128
129                            output = Some(self.build_from_eq_meta(&meta)?);
130                        }
131                    }
132                }
133            }
134        }
135
136        Ok(output.unwrap_or(TypeAttribute {
137            bound: Bound::Auto
138        }))
139    }
140}