educe/trait_handlers/default/models/
field_attribute.rs

1use proc_macro2::Span;
2use syn::{punctuated::Punctuated, spanned::Spanned, Attribute, Expr, Meta, Token, Type};
3
4use crate::{
5    common::expr::{auto_adjust_expr, meta_2_expr},
6    panic,
7    supported_traits::Trait,
8};
9
10pub(crate) struct FieldAttribute {
11    pub(crate) flag:       bool,
12    pub(crate) expression: Option<Expr>,
13    pub(crate) span:       Span,
14}
15
16pub(crate) struct FieldAttributeBuilder {
17    pub(crate) enable_flag:       bool,
18    pub(crate) enable_expression: bool,
19}
20
21impl FieldAttributeBuilder {
22    pub(crate) fn build_from_default_meta(
23        &self,
24        meta: &Meta,
25        ty: &Type,
26    ) -> syn::Result<FieldAttribute> {
27        debug_assert!(meta.path().is_ident("Default"));
28
29        let mut flag = false;
30        let mut expression = None;
31
32        let correct_usage_for_default_attribute = {
33            let mut usage = vec![];
34
35            if self.enable_flag {
36                usage.push(stringify!(#[educe(Default)]));
37            }
38
39            if self.enable_expression {
40                usage.push(stringify!(#[educe(Default = expr)]));
41
42                usage.push(stringify!(#[educe(Default(expression = expr))]));
43            }
44
45            usage
46        };
47
48        match meta {
49            Meta::Path(_) => {
50                if !self.enable_flag {
51                    return Err(panic::attribute_incorrect_format(
52                        meta.path().get_ident().unwrap(),
53                        &correct_usage_for_default_attribute,
54                    ));
55                }
56
57                flag = true;
58            },
59            Meta::NameValue(name_value) => {
60                if !self.enable_expression {
61                    return Err(panic::attribute_incorrect_format(
62                        meta.path().get_ident().unwrap(),
63                        &correct_usage_for_default_attribute,
64                    ));
65                }
66
67                expression = Some(auto_adjust_expr(name_value.value.clone(), Some(ty)));
68            },
69            Meta::List(list) => {
70                let result =
71                    list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
72
73                let mut expression_is_set = false;
74
75                let mut handler = |meta: Meta| -> syn::Result<bool> {
76                    if let Some(ident) = meta.path().get_ident() {
77                        match ident.to_string().as_str() {
78                            "expression" | "expr" => {
79                                if !self.enable_expression {
80                                    return Ok(false);
81                                }
82
83                                let v = meta_2_expr(&meta)?;
84
85                                if expression_is_set {
86                                    return Err(panic::parameter_reset(ident));
87                                }
88
89                                expression_is_set = true;
90
91                                expression = Some(auto_adjust_expr(v, Some(ty)));
92
93                                return Ok(true);
94                            },
95                            _ => (),
96                        }
97                    }
98
99                    Ok(false)
100                };
101
102                for p in result {
103                    if !handler(p)? {
104                        return Err(panic::attribute_incorrect_format(
105                            meta.path().get_ident().unwrap(),
106                            &correct_usage_for_default_attribute,
107                        ));
108                    }
109                }
110            },
111        }
112
113        Ok(FieldAttribute {
114            flag,
115            expression,
116            span: meta.span(),
117        })
118    }
119
120    pub(crate) fn build_from_attributes(
121        &self,
122        attributes: &[Attribute],
123        traits: &[Trait],
124        ty: &Type,
125    ) -> syn::Result<FieldAttribute> {
126        let mut output = None;
127
128        for attribute in attributes.iter() {
129            let path = attribute.path();
130
131            if path.is_ident("educe") {
132                if let Meta::List(list) = &attribute.meta {
133                    let result =
134                        list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
135
136                    for meta in result {
137                        let path = meta.path();
138
139                        let t = match Trait::from_path(path) {
140                            Some(t) => t,
141                            None => return Err(panic::unsupported_trait(meta.path())),
142                        };
143
144                        if !traits.contains(&t) {
145                            return Err(panic::trait_not_used(path.get_ident().unwrap()));
146                        }
147
148                        if t == Trait::Default {
149                            if output.is_some() {
150                                return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
151                            }
152
153                            output = Some(self.build_from_default_meta(&meta, ty)?);
154                        }
155                    }
156                }
157            }
158        }
159
160        Ok(output.unwrap_or(FieldAttribute {
161            flag:       false,
162            expression: None,
163            span:       Span::call_site(),
164        }))
165    }
166}