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}