educe/trait_handlers/default/models/
type_attribute.rs

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