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}