educe/trait_handlers/default/models/
type_attribute.rs

1use proc_macro2::Span;
2use syn::{punctuated::Punctuated, spanned::Spanned, Attribute, Expr, Meta, Token};
3
4use crate::{
5    common::{
6        bound::Bound,
7        expr::{auto_adjust_expr, meta_2_expr},
8        ident_bool::meta_2_bool_allow_path,
9    },
10    panic, Trait,
11};
12
13pub(crate) struct TypeAttribute {
14    pub(crate) flag:       bool,
15    pub(crate) new:        bool,
16    pub(crate) expression: Option<Expr>,
17    pub(crate) bound:      Bound,
18    pub(crate) span:       Span,
19}
20
21#[derive(Debug)]
22pub(crate) struct TypeAttributeBuilder {
23    pub(crate) enable_flag:       bool,
24    pub(crate) enable_new:        bool,
25    pub(crate) enable_expression: bool,
26    pub(crate) enable_bound:      bool,
27}
28
29impl TypeAttributeBuilder {
30    pub(crate) fn build_from_default_meta(&self, meta: &Meta) -> syn::Result<TypeAttribute> {
31        debug_assert!(meta.path().is_ident("Default"));
32
33        let mut flag = false;
34        let mut new = false;
35        let mut expression = None;
36        let mut bound = Bound::Auto;
37
38        let correct_usage_for_default_attribute = {
39            let mut usage = vec![];
40
41            if self.enable_flag {
42                usage.push(stringify!(#[educe(Default)]));
43            }
44
45            if self.enable_new {
46                usage.push(stringify!(#[educe(Default(new))]));
47            }
48
49            if self.enable_expression {
50                usage.push(stringify!(#[educe(Default(expression = expr))]));
51            }
52
53            if self.enable_bound {
54                usage.push(stringify!(#[educe(Default(bound(where_predicates)))]));
55                usage.push(stringify!(#[educe(Default(bound = false))]));
56            }
57
58            usage
59        };
60
61        match meta {
62            Meta::Path(_) => {
63                if !self.enable_flag {
64                    return Err(panic::attribute_incorrect_format(
65                        meta.path().get_ident().unwrap(),
66                        &correct_usage_for_default_attribute,
67                    ));
68                }
69
70                flag = true;
71            },
72            Meta::NameValue(_) => {
73                return Err(panic::attribute_incorrect_format(
74                    meta.path().get_ident().unwrap(),
75                    &correct_usage_for_default_attribute,
76                ));
77            },
78            Meta::List(list) => {
79                let result =
80                    list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
81
82                let mut new_is_set = false;
83                let mut expression_is_set = false;
84                let mut bound_is_set = false;
85
86                let mut handler = |meta: Meta| -> syn::Result<bool> {
87                    if let Some(ident) = meta.path().get_ident() {
88                        match ident.to_string().as_str() {
89                            "new" => {
90                                if !self.enable_new {
91                                    return Ok(false);
92                                }
93
94                                let v = meta_2_bool_allow_path(&meta)?;
95
96                                if new_is_set {
97                                    return Err(panic::parameter_reset(ident));
98                                }
99
100                                new_is_set = true;
101
102                                new = v;
103
104                                return Ok(true);
105                            },
106                            "expression" | "expr" => {
107                                if !self.enable_expression {
108                                    return Ok(false);
109                                }
110
111                                let v = meta_2_expr(&meta)?;
112
113                                if expression_is_set {
114                                    return Err(panic::parameter_reset(ident));
115                                }
116
117                                expression_is_set = true;
118
119                                expression = Some(auto_adjust_expr(v, None));
120
121                                return Ok(true);
122                            },
123                            "bound" => {
124                                if !self.enable_bound {
125                                    return Ok(false);
126                                }
127
128                                let v = Bound::from_meta(&meta)?;
129
130                                if bound_is_set {
131                                    return Err(panic::parameter_reset(ident));
132                                }
133
134                                bound_is_set = true;
135
136                                bound = v;
137
138                                return Ok(true);
139                            },
140                            _ => (),
141                        }
142                    }
143
144                    Ok(false)
145                };
146
147                for p in result {
148                    if !handler(p)? {
149                        return Err(panic::attribute_incorrect_format(
150                            meta.path().get_ident().unwrap(),
151                            &correct_usage_for_default_attribute,
152                        ));
153                    }
154                }
155            },
156        }
157
158        Ok(TypeAttribute {
159            flag,
160            new,
161            expression,
162            bound,
163            span: meta.span(),
164        })
165    }
166
167    pub(crate) fn build_from_attributes(
168        &self,
169        attributes: &[Attribute],
170        traits: &[Trait],
171    ) -> syn::Result<TypeAttribute> {
172        let mut output = None;
173
174        for attribute in attributes.iter() {
175            let path = attribute.path();
176
177            if path.is_ident("educe") {
178                if let Meta::List(list) = &attribute.meta {
179                    let result =
180                        list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
181
182                    for meta in result {
183                        let path = meta.path();
184
185                        let t = match Trait::from_path(path) {
186                            Some(t) => t,
187                            None => return Err(panic::unsupported_trait(meta.path())),
188                        };
189
190                        if !traits.contains(&t) {
191                            return Err(panic::trait_not_used(path.get_ident().unwrap()));
192                        }
193
194                        if t == Trait::Default {
195                            if output.is_some() {
196                                return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
197                            }
198
199                            output = Some(self.build_from_default_meta(&meta)?);
200                        }
201                    }
202                }
203            }
204        }
205
206        Ok(output.unwrap_or(TypeAttribute {
207            flag:       false,
208            new:        false,
209            expression: None,
210            bound:      Bound::Auto,
211            span:       Span::call_site(),
212        }))
213    }
214}