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}