educe/trait_handlers/hash/models/
type_attribute.rs

1use quote::{quote, ToTokens};
2use syn::{
3    punctuated::Punctuated, token::Comma, Attribute, GenericParam, Lit, Meta, NestedMeta,
4    WherePredicate,
5};
6
7use super::super::super::{
8    create_where_predicates_from_generic_parameters, create_where_predicates_from_lit_str,
9};
10use crate::{panic, Trait};
11
12#[derive(Clone)]
13pub enum TypeAttributeBound {
14    None,
15    Auto,
16    Custom(Punctuated<WherePredicate, Comma>),
17}
18
19impl TypeAttributeBound {
20    pub fn into_punctuated_where_predicates_by_generic_parameters(
21        self,
22        params: &Punctuated<GenericParam, Comma>,
23    ) -> Punctuated<WherePredicate, Comma> {
24        match self {
25            TypeAttributeBound::None => Punctuated::new(),
26            TypeAttributeBound::Auto => create_where_predicates_from_generic_parameters(
27                params,
28                &syn::parse2(quote!(core::hash::Hash)).unwrap(),
29            ),
30            TypeAttributeBound::Custom(where_predicates) => where_predicates,
31        }
32    }
33}
34
35#[derive(Clone)]
36pub struct TypeAttribute {
37    pub flag:  bool,
38    pub bound: TypeAttributeBound,
39}
40
41#[derive(Debug, Clone)]
42pub struct TypeAttributeBuilder {
43    pub enable_flag:  bool,
44    pub enable_bound: bool,
45}
46
47impl TypeAttributeBuilder {
48    #[allow(clippy::wrong_self_convention)]
49    pub fn from_hash_meta(&self, meta: &Meta) -> TypeAttribute {
50        let mut flag = false;
51        let mut bound = TypeAttributeBound::None;
52
53        let correct_usage_for_hash_attribute = {
54            let mut usage = vec![];
55
56            if self.enable_flag {
57                usage.push(stringify!(#[educe(Hash)]));
58            }
59
60            usage
61        };
62
63        let correct_usage_for_bound = {
64            let usage = vec![
65                stringify!(#[educe(Hash(bound))]),
66                stringify!(#[educe(Hash(bound = "where_predicates"))]),
67                stringify!(#[educe(Hash(bound("where_predicates")))]),
68            ];
69
70            usage
71        };
72
73        match meta {
74            Meta::List(list) => {
75                let mut bound_is_set = false;
76
77                for p in list.nested.iter() {
78                    match p {
79                        NestedMeta::Meta(meta) => {
80                            let meta_name = meta.path().into_token_stream().to_string();
81
82                            match meta_name.as_str() {
83                                "bound" => {
84                                    if !self.enable_bound {
85                                        panic::unknown_parameter("Hash", meta_name.as_str());
86                                    }
87
88                                    match meta {
89                                        Meta::List(list) => {
90                                            for p in list.nested.iter() {
91                                                match p {
92                                                    NestedMeta::Lit(Lit::Str(s)) => {
93                                                        if bound_is_set {
94                                                            panic::reset_parameter(
95                                                                meta_name.as_str(),
96                                                            );
97                                                        }
98
99                                                        bound_is_set = true;
100
101                                                        let where_predicates =
102                                                            create_where_predicates_from_lit_str(s);
103
104                                                        bound = match where_predicates {
105                                                            Some(where_predicates) => {
106                                                                TypeAttributeBound::Custom(
107                                                                    where_predicates,
108                                                                )
109                                                            },
110                                                            None => panic::empty_parameter(
111                                                                meta_name.as_str(),
112                                                            ),
113                                                        };
114                                                    },
115                                                    _ => panic::parameter_incorrect_format(
116                                                        meta_name.as_str(),
117                                                        &correct_usage_for_bound,
118                                                    ),
119                                                }
120                                            }
121                                        },
122                                        Meta::NameValue(named_value) => {
123                                            let lit = &named_value.lit;
124
125                                            match lit {
126                                                Lit::Str(s) => {
127                                                    if bound_is_set {
128                                                        panic::reset_parameter(meta_name.as_str());
129                                                    }
130
131                                                    bound_is_set = true;
132
133                                                    let where_predicates =
134                                                        create_where_predicates_from_lit_str(s);
135
136                                                    bound = match where_predicates {
137                                                        Some(where_predicates) => {
138                                                            TypeAttributeBound::Custom(
139                                                                where_predicates,
140                                                            )
141                                                        },
142                                                        None => panic::empty_parameter(
143                                                            meta_name.as_str(),
144                                                        ),
145                                                    };
146                                                },
147                                                _ => panic::parameter_incorrect_format(
148                                                    meta_name.as_str(),
149                                                    &correct_usage_for_bound,
150                                                ),
151                                            }
152                                        },
153                                        Meta::Path(_) => {
154                                            if bound_is_set {
155                                                panic::reset_parameter(meta_name.as_str());
156                                            }
157
158                                            bound_is_set = true;
159
160                                            bound = TypeAttributeBound::Auto;
161                                        },
162                                    }
163                                },
164                                _ => panic::unknown_parameter("Hash", meta_name.as_str()),
165                            }
166                        },
167                        _ => panic::attribute_incorrect_format(
168                            "Hash",
169                            &correct_usage_for_hash_attribute,
170                        ),
171                    }
172                }
173            },
174            Meta::NameValue(_) => {
175                panic::attribute_incorrect_format("Hash", &correct_usage_for_hash_attribute)
176            },
177            Meta::Path(_) => {
178                if !self.enable_flag {
179                    panic::attribute_incorrect_format("Hash", &correct_usage_for_hash_attribute);
180                }
181
182                flag = true;
183            },
184        }
185
186        TypeAttribute {
187            flag,
188            bound,
189        }
190    }
191
192    #[allow(clippy::wrong_self_convention)]
193    pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> TypeAttribute {
194        let mut result = None;
195
196        for attribute in attributes.iter() {
197            if attribute.path.is_ident("educe") {
198                let meta = attribute.parse_meta().unwrap();
199
200                match meta {
201                    Meta::List(list) => {
202                        for p in list.nested.iter() {
203                            match p {
204                                NestedMeta::Meta(meta) => {
205                                    let meta_name = meta.path().into_token_stream().to_string();
206
207                                    let t = Trait::from_str(meta_name);
208
209                                    if traits.binary_search(&t).is_err() {
210                                        panic::trait_not_used(t);
211                                    }
212
213                                    if t == Trait::Hash {
214                                        if result.is_some() {
215                                            panic::reuse_a_trait(t);
216                                        }
217
218                                        result = Some(self.from_hash_meta(meta));
219                                    }
220                                },
221                                _ => panic::educe_format_incorrect(),
222                            }
223                        }
224                    },
225                    _ => panic::educe_format_incorrect(),
226                }
227            }
228        }
229
230        result.unwrap_or(TypeAttribute {
231            flag: false, bound: TypeAttributeBound::None
232        })
233    }
234}