educe/trait_handlers/ord/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 compare_method: Option<String>,
11 pub compare_trait: Option<String>,
12 pub rank: isize,
13}
14
15#[derive(Debug, Clone)]
16pub struct FieldAttributeBuilder {
17 pub enable_ignore: bool,
18 pub enable_impl: bool,
19 pub rank: isize,
20 pub enable_rank: bool,
21}
22
23impl FieldAttributeBuilder {
24 #[allow(clippy::wrong_self_convention)]
25 pub fn from_ord_meta(&self, meta: &Meta) -> FieldAttribute {
26 let mut ignore = false;
27
28 let mut compare_method = None;
29 let mut compare_trait = None;
30
31 let mut rank = self.rank;
32
33 let correct_usage_for_ord_attribute = {
34 let mut usage = vec![];
35
36 if self.enable_ignore {
37 usage.push(stringify!(#[educe(Ord = false)]));
38 usage.push(stringify!(#[educe(Ord(false))]));
39 }
40
41 usage
42 };
43
44 let correct_usage_for_ignore = {
45 let usage = vec![stringify!(#[educe(Ord(ignore))])];
46
47 usage
48 };
49
50 let correct_usage_for_impl = {
51 let usage = vec![
52 stringify!(#[educe(Ord(method = "path_to_method"))]),
53 stringify!(#[educe(Ord(trait = "path_to_trait"))]),
54 stringify!(#[educe(Ord(trait = "path_to_trait", method = "path_to_method_in_trait"))]),
55 stringify!(#[educe(Ord(method("path_to_method")))]),
56 stringify!(#[educe(Ord(trait("path_to_trait")))]),
57 stringify!(#[educe(Ord(trait("path_to_trait"), method("path_to_method_in_trait")))]),
58 ];
59
60 usage
61 };
62
63 let correct_usage_for_rank = {
64 let usage = vec![
65 stringify!(#[educe(Ord(rank = priority_value))]),
66 stringify!(#[educe(Ord(rank(priority_value)))]),
67 ];
68
69 usage
70 };
71
72 let mut rank_is_set = false;
73
74 match meta {
75 Meta::List(list) => {
76 let mut ignore_is_set = false;
77
78 for p in list.nested.iter() {
79 match p {
80 NestedMeta::Meta(meta) => {
81 let meta_name = meta.path().into_token_stream().to_string();
82
83 match meta_name.as_str() {
84 "ignore" => {
85 if !self.enable_ignore {
86 panic::unknown_parameter("Ord", meta_name.as_str());
87 }
88
89 match meta {
90 Meta::Path(_) => {
91 if ignore_is_set {
92 panic::reset_parameter(meta_name.as_str());
93 }
94
95 ignore_is_set = true;
96
97 ignore = true;
98 },
99 _ => panic::parameter_incorrect_format(
100 meta_name.as_str(),
101 &correct_usage_for_ignore,
102 ),
103 }
104 },
105 "method" => {
106 if !self.enable_impl {
107 panic::unknown_parameter("Ord", meta_name.as_str());
108 }
109
110 match meta {
111 Meta::List(list) => {
112 for p in list.nested.iter() {
113 match p {
114 NestedMeta::Lit(Lit::Str(s)) => {
115 if compare_method.is_some() {
116 panic::reset_parameter(
117 meta_name.as_str(),
118 );
119 }
120
121 let s = create_path_string_from_lit_str(s);
122
123 if let Some(s) = s {
124 compare_method = Some(s);
125 } else {
126 panic::empty_parameter(
127 meta_name.as_str(),
128 );
129 }
130 },
131 _ => panic::parameter_incorrect_format(
132 meta_name.as_str(),
133 &correct_usage_for_impl,
134 ),
135 }
136 }
137 },
138 Meta::NameValue(named_value) => {
139 let lit = &named_value.lit;
140
141 match lit {
142 Lit::Str(s) => {
143 if compare_method.is_some() {
144 panic::reset_parameter(meta_name.as_str());
145 }
146
147 let s = create_path_string_from_lit_str(s);
148
149 if let Some(s) = s {
150 compare_method = Some(s);
151 } else {
152 panic::empty_parameter(meta_name.as_str());
153 }
154 },
155 _ => panic::parameter_incorrect_format(
156 meta_name.as_str(),
157 &correct_usage_for_impl,
158 ),
159 }
160 },
161 _ => panic::parameter_incorrect_format(
162 meta_name.as_str(),
163 &correct_usage_for_impl,
164 ),
165 }
166 },
167 "trait" => {
168 if !self.enable_impl {
169 panic::unknown_parameter("Ord", meta_name.as_str());
170 }
171
172 match meta {
173 Meta::List(list) => {
174 for p in list.nested.iter() {
175 match p {
176 NestedMeta::Lit(Lit::Str(s)) => {
177 if compare_trait.is_some() {
178 panic::reset_parameter(
179 meta_name.as_str(),
180 );
181 }
182
183 let s = create_path_string_from_lit_str(s);
184
185 if let Some(s) = s {
186 compare_trait = Some(s);
187 } else {
188 panic::empty_parameter(
189 meta_name.as_str(),
190 );
191 }
192 },
193 _ => panic::parameter_incorrect_format(
194 meta_name.as_str(),
195 &correct_usage_for_impl,
196 ),
197 }
198 }
199 },
200 Meta::NameValue(named_value) => {
201 let lit = &named_value.lit;
202
203 match lit {
204 Lit::Str(s) => {
205 if compare_trait.is_some() {
206 panic::reset_parameter(meta_name.as_str());
207 }
208
209 let s = create_path_string_from_lit_str(s);
210
211 if let Some(s) = s {
212 compare_trait = Some(s);
213 } else {
214 panic::empty_parameter(meta_name.as_str());
215 }
216 },
217 _ => panic::parameter_incorrect_format(
218 meta_name.as_str(),
219 &correct_usage_for_impl,
220 ),
221 }
222 },
223 _ => panic::parameter_incorrect_format(
224 meta_name.as_str(),
225 &correct_usage_for_impl,
226 ),
227 }
228 },
229 "rank" => {
230 if !self.enable_rank {
231 panic::unknown_parameter("Ord", meta_name.as_str());
232 }
233
234 match meta {
235 Meta::List(list) => {
236 for p in list.nested.iter() {
237 match p {
238 NestedMeta::Lit(Lit::Int(i)) => {
239 if rank_is_set {
240 panic::reset_parameter(
241 meta_name.as_str(),
242 );
243 }
244
245 rank_is_set = true;
246
247 rank = i.base10_parse().unwrap();
248 },
249 _ => panic::parameter_incorrect_format(
250 meta_name.as_str(),
251 &correct_usage_for_rank,
252 ),
253 }
254 }
255 },
256 Meta::NameValue(named_value) => {
257 let lit = &named_value.lit;
258
259 match lit {
260 Lit::Int(i) => {
261 if rank_is_set {
262 panic::reset_parameter(meta_name.as_str());
263 }
264
265 rank_is_set = true;
266
267 rank = i.base10_parse().unwrap();
268 },
269 _ => panic::parameter_incorrect_format(
270 meta_name.as_str(),
271 &correct_usage_for_rank,
272 ),
273 }
274 },
275 _ => panic::parameter_incorrect_format(
276 meta_name.as_str(),
277 &correct_usage_for_rank,
278 ),
279 }
280 },
281 _ => panic::unknown_parameter("Ord", meta_name.as_str()),
282 }
283 },
284 _ => panic::attribute_incorrect_format(
285 "Ord",
286 &correct_usage_for_ord_attribute,
287 ),
288 }
289 }
290 },
291 _ => panic::attribute_incorrect_format("Ord", &correct_usage_for_ord_attribute),
292 }
293
294 if compare_trait.is_some() && compare_method.is_none() {
295 compare_method = Some("cmp".to_string());
296 }
297
298 if ignore && rank_is_set {
299 panic::ignore_ranked_field();
300 }
301
302 FieldAttribute {
303 ignore,
304 compare_method,
305 compare_trait,
306 rank,
307 }
308 }
309
310 #[allow(clippy::wrong_self_convention)]
311 pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> FieldAttribute {
312 let mut result = None;
313
314 for attribute in attributes.iter() {
315 if attribute.path.is_ident("educe") {
316 let meta = attribute.parse_meta().unwrap();
317
318 match meta {
319 Meta::List(list) => {
320 for p in list.nested.iter() {
321 match p {
322 NestedMeta::Meta(meta) => {
323 let meta_name = meta.path().into_token_stream().to_string();
324
325 let t = Trait::from_str(meta_name);
326
327 if traits.binary_search(&t).is_err() {
328 panic::trait_not_used(t);
329 }
330
331 if t == Trait::Ord {
332 if result.is_some() {
333 panic::reuse_a_trait(t);
334 }
335
336 result = Some(self.from_ord_meta(meta));
337 }
338 },
339 _ => panic::educe_format_incorrect(),
340 }
341 }
342 },
343 _ => panic::educe_format_incorrect(),
344 }
345 }
346 }
347
348 result.unwrap_or(FieldAttribute {
349 ignore: false,
350 compare_method: None,
351 compare_trait: None,
352 rank: self.rank,
353 })
354 }
355}