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}