educe/trait_handlers/hash/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 struct FieldAttribute {
9    pub ignore:      bool,
10    pub hash_method: Option<String>,
11    pub hash_trait:  Option<String>,
12}
13
14#[derive(Debug, Clone)]
15pub struct FieldAttributeBuilder {
16    pub enable_ignore: bool,
17    pub enable_impl:   bool,
18}
19
20impl FieldAttributeBuilder {
21    #[allow(clippy::wrong_self_convention)]
22    pub fn from_hash_meta(&self, meta: &Meta) -> FieldAttribute {
23        let mut ignore = false;
24
25        let mut hash_method = None;
26        let mut hash_trait = None;
27
28        let correct_usage_for_hash_attribute = {
29            let mut usage = vec![];
30
31            if self.enable_ignore {
32                usage.push(stringify!(#[educe(Hash = false)]));
33                usage.push(stringify!(#[educe(Hash(false))]));
34            }
35
36            usage
37        };
38
39        let correct_usage_for_ignore = {
40            let usage = vec![stringify!(#[educe(Hash(ignore))])];
41
42            usage
43        };
44
45        let correct_usage_for_impl = {
46            let usage = vec![
47                stringify!(#[educe(Hash(method = "path_to_method"))]),
48                stringify!(#[educe(Hash(trait = "path_to_trait"))]),
49                stringify!(#[educe(Hash(trait = "path_to_trait", method = "path_to_method_in_trait"))]),
50                stringify!(#[educe(Hash(method("path_to_method")))]),
51                stringify!(#[educe(Hash(trait("path_to_trait")))]),
52                stringify!(#[educe(Hash(trait("path_to_trait"), method("path_to_method_in_trait")))]),
53            ];
54
55            usage
56        };
57
58        match meta {
59            Meta::List(list) => {
60                let mut ignore_is_set = false;
61
62                for p in list.nested.iter() {
63                    match p {
64                        NestedMeta::Meta(meta) => {
65                            let meta_name = meta.path().into_token_stream().to_string();
66
67                            match meta_name.as_str() {
68                                "ignore" => {
69                                    if !self.enable_ignore {
70                                        panic::unknown_parameter("Hash", meta_name.as_str());
71                                    }
72
73                                    match meta {
74                                        Meta::Path(_) => {
75                                            if ignore_is_set {
76                                                panic::reset_parameter(meta_name.as_str());
77                                            }
78
79                                            ignore_is_set = true;
80
81                                            ignore = true;
82                                        },
83                                        _ => panic::parameter_incorrect_format(
84                                            meta_name.as_str(),
85                                            &correct_usage_for_ignore,
86                                        ),
87                                    }
88                                },
89                                "method" => {
90                                    if !self.enable_impl {
91                                        panic::unknown_parameter("Hash", meta_name.as_str());
92                                    }
93
94                                    match meta {
95                                        Meta::List(list) => {
96                                            for p in list.nested.iter() {
97                                                match p {
98                                                    NestedMeta::Lit(Lit::Str(s)) => {
99                                                        if hash_method.is_some() {
100                                                            panic::reset_parameter(
101                                                                meta_name.as_str(),
102                                                            );
103                                                        }
104
105                                                        let s = create_path_string_from_lit_str(s);
106
107                                                        if let Some(s) = s {
108                                                            hash_method = Some(s);
109                                                        } else {
110                                                            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_impl,
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 hash_method.is_some() {
128                                                        panic::reset_parameter(meta_name.as_str());
129                                                    }
130
131                                                    let s = create_path_string_from_lit_str(s);
132
133                                                    if let Some(s) = s {
134                                                        hash_method = Some(s);
135                                                    } else {
136                                                        panic::empty_parameter(meta_name.as_str());
137                                                    }
138                                                },
139                                                _ => panic::parameter_incorrect_format(
140                                                    meta_name.as_str(),
141                                                    &correct_usage_for_impl,
142                                                ),
143                                            }
144                                        },
145                                        _ => panic::parameter_incorrect_format(
146                                            meta_name.as_str(),
147                                            &correct_usage_for_impl,
148                                        ),
149                                    }
150                                },
151                                "trait" => {
152                                    if !self.enable_impl {
153                                        panic::unknown_parameter("Hash", meta_name.as_str());
154                                    }
155
156                                    match meta {
157                                        Meta::List(list) => {
158                                            for p in list.nested.iter() {
159                                                match p {
160                                                    NestedMeta::Lit(Lit::Str(s)) => {
161                                                        if hash_trait.is_some() {
162                                                            panic::reset_parameter(
163                                                                meta_name.as_str(),
164                                                            );
165                                                        }
166
167                                                        let s = create_path_string_from_lit_str(s);
168
169                                                        if let Some(s) = s {
170                                                            hash_trait = Some(s);
171                                                        } else {
172                                                            panic::empty_parameter(
173                                                                meta_name.as_str(),
174                                                            );
175                                                        }
176                                                    },
177                                                    _ => panic::parameter_incorrect_format(
178                                                        meta_name.as_str(),
179                                                        &correct_usage_for_impl,
180                                                    ),
181                                                }
182                                            }
183                                        },
184                                        Meta::NameValue(named_value) => {
185                                            let lit = &named_value.lit;
186
187                                            match lit {
188                                                Lit::Str(s) => {
189                                                    if hash_trait.is_some() {
190                                                        panic::reset_parameter(meta_name.as_str());
191                                                    }
192
193                                                    let s = create_path_string_from_lit_str(s);
194
195                                                    if let Some(s) = s {
196                                                        hash_trait = Some(s);
197                                                    } else {
198                                                        panic::empty_parameter(meta_name.as_str());
199                                                    }
200                                                },
201                                                _ => panic::parameter_incorrect_format(
202                                                    meta_name.as_str(),
203                                                    &correct_usage_for_impl,
204                                                ),
205                                            }
206                                        },
207                                        _ => panic::parameter_incorrect_format(
208                                            meta_name.as_str(),
209                                            &correct_usage_for_impl,
210                                        ),
211                                    }
212                                },
213                                _ => panic::unknown_parameter("Hash", meta_name.as_str()),
214                            }
215                        },
216                        _ => panic::attribute_incorrect_format(
217                            "Hash",
218                            &correct_usage_for_hash_attribute,
219                        ),
220                    }
221                }
222            },
223            _ => panic::attribute_incorrect_format("Hash", &correct_usage_for_hash_attribute),
224        }
225
226        if hash_trait.is_some() && hash_method.is_none() {
227            hash_method = Some("hash".to_string());
228        }
229
230        FieldAttribute {
231            ignore,
232            hash_method,
233            hash_trait,
234        }
235    }
236
237    #[allow(clippy::wrong_self_convention)]
238    pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> FieldAttribute {
239        let mut result = None;
240
241        for attribute in attributes.iter() {
242            if attribute.path.is_ident("educe") {
243                let meta = attribute.parse_meta().unwrap();
244
245                match meta {
246                    Meta::List(list) => {
247                        for p in list.nested.iter() {
248                            match p {
249                                NestedMeta::Meta(meta) => {
250                                    let meta_name = meta.path().into_token_stream().to_string();
251
252                                    let t = Trait::from_str(meta_name);
253
254                                    if traits.binary_search(&t).is_err() {
255                                        panic::trait_not_used(t);
256                                    }
257
258                                    if t == Trait::Hash {
259                                        if result.is_some() {
260                                            panic::reuse_a_trait(t);
261                                        }
262
263                                        result = Some(self.from_hash_meta(meta));
264                                    }
265                                },
266                                _ => panic::educe_format_incorrect(),
267                            }
268                        }
269                    },
270                    _ => panic::educe_format_incorrect(),
271                }
272            }
273        }
274
275        result.unwrap_or(FieldAttribute {
276            ignore:      false,
277            hash_method: None,
278            hash_trait:  None,
279        })
280    }
281}