educe/trait_handlers/debug/models/type_attribute.rs
1use quote::{quote, ToTokens};
2use syn::{
3 punctuated::Punctuated, token::Comma, Attribute, GenericParam, Ident, Lit, Meta, NestedMeta,
4 WherePredicate,
5};
6
7use super::super::super::{
8 create_path_string_from_lit_str, create_where_predicates_from_generic_parameters,
9 create_where_predicates_from_lit_str,
10};
11use crate::{panic, Trait};
12
13#[derive(Debug, Clone)]
14pub enum TypeAttributeName {
15 Disable,
16 Default,
17 Custom(String),
18}
19
20impl TypeAttributeName {
21 pub fn into_string_by_ident(self, ident: &Ident) -> String {
22 match self {
23 TypeAttributeName::Disable => String::new(),
24 TypeAttributeName::Default => ident.to_string(),
25 TypeAttributeName::Custom(s) => s,
26 }
27 }
28}
29
30#[derive(Clone)]
31pub enum TypeAttributeBound {
32 None,
33 Auto,
34 Custom(Punctuated<WherePredicate, Comma>),
35}
36
37impl TypeAttributeBound {
38 pub fn into_punctuated_where_predicates_by_generic_parameters(
39 self,
40 params: &Punctuated<GenericParam, Comma>,
41 ) -> Punctuated<WherePredicate, Comma> {
42 match self {
43 TypeAttributeBound::None => Punctuated::new(),
44 TypeAttributeBound::Auto => create_where_predicates_from_generic_parameters(
45 params,
46 &syn::parse2(quote!(core::fmt::Debug)).unwrap(),
47 ),
48 TypeAttributeBound::Custom(where_predicates) => where_predicates,
49 }
50 }
51}
52
53#[derive(Clone)]
54pub struct TypeAttribute {
55 pub flag: bool,
56 pub name: TypeAttributeName,
57 pub named_field: bool,
58 pub bound: TypeAttributeBound,
59}
60
61#[derive(Debug, Clone)]
62pub struct TypeAttributeBuilder {
63 pub enable_flag: bool,
64 pub name: TypeAttributeName,
65 pub enable_name: bool,
66 pub named_field: bool,
67 pub enable_named_field: bool,
68 pub enable_bound: bool,
69}
70
71impl TypeAttributeBuilder {
72 #[allow(clippy::wrong_self_convention)]
73 pub fn from_debug_meta(&self, meta: &Meta) -> TypeAttribute {
74 let mut flag = false;
75 let mut name = self.name.clone();
76 let mut named_field = self.named_field;
77 let mut bound = TypeAttributeBound::None;
78
79 let correct_usage_for_debug_attribute = {
80 let mut usage = vec![];
81
82 if self.enable_flag {
83 usage.push(stringify!(#[educe(Default)]));
84 }
85
86 if self.enable_name {
87 usage.push(stringify!(#[educe(Debug = "new_name")]));
88 usage.push(stringify!(#[educe(Debug("new_name"))]));
89 }
90
91 if self.enable_bound {
92 usage.push(stringify!(#[educe(Debug(ignore))]));
93 }
94
95 usage
96 };
97
98 let correct_usage_for_name = {
99 let mut usage = vec![
100 stringify!(#[educe(Debug(name = "new_name"))]),
101 stringify!(#[educe(Debug(name("new_name")))]),
102 ];
103
104 if let TypeAttributeName::Disable = &name {
105 usage.push(stringify!(#[educe(Debug(name = true))]));
106 usage.push(stringify!(#[educe(Debug(name(true)))]));
107 } else {
108 usage.push(stringify!(#[educe(Debug(name = false))]));
109 usage.push(stringify!(#[educe(Debug(name(false)))]));
110 }
111
112 usage
113 };
114
115 let correct_usage_for_named_field = {
116 let mut usage = vec![];
117
118 if !self.named_field {
119 usage.push(stringify!(#[educe(Debug(named_field = true))]));
120 usage.push(stringify!(#[educe(Debug(named_field(true)))]));
121 } else {
122 usage.push(stringify!(#[educe(Debug(named_field = false))]));
123 usage.push(stringify!(#[educe(Debug(named_field(false)))]));
124 }
125
126 usage
127 };
128
129 let correct_usage_for_bound = {
130 let usage = vec![
131 stringify!(#[educe(Debug(bound))]),
132 stringify!(#[educe(Debug(bound = "where_predicates"))]),
133 stringify!(#[educe(Debug(bound("where_predicates")))]),
134 ];
135
136 usage
137 };
138
139 match meta {
140 Meta::List(list) => {
141 let mut name_is_set = false;
142 let mut named_field_is_set = false;
143 let mut bound_is_set = false;
144
145 for p in list.nested.iter() {
146 match p {
147 NestedMeta::Meta(meta) => {
148 let meta_name = meta.path().into_token_stream().to_string();
149
150 match meta_name.as_str() {
151 "name" | "rename" => {
152 if !self.enable_name {
153 panic::unknown_parameter("Debug", meta_name.as_str());
154 }
155
156 match meta {
157 Meta::List(list) => {
158 for p in list.nested.iter() {
159 match p {
160 NestedMeta::Lit(lit) => match lit {
161 Lit::Str(s) => {
162 if name_is_set {
163 panic::reset_parameter(
164 meta_name.as_str(),
165 );
166 }
167
168 name_is_set = true;
169
170 let s =
171 create_path_string_from_lit_str(s);
172
173 name = match s {
174 Some(s) => {
175 TypeAttributeName::Custom(s)
176 },
177 None => TypeAttributeName::Disable,
178 };
179 },
180 Lit::Bool(s) => {
181 if name_is_set {
182 panic::reset_parameter(
183 meta_name.as_str(),
184 );
185 }
186
187 name_is_set = true;
188
189 if s.value {
190 name = TypeAttributeName::Default;
191 } else {
192 name = TypeAttributeName::Disable;
193 }
194 },
195 _ => panic::parameter_incorrect_format(
196 meta_name.as_str(),
197 &correct_usage_for_name,
198 ),
199 },
200 _ => panic::parameter_incorrect_format(
201 meta_name.as_str(),
202 &correct_usage_for_name,
203 ),
204 }
205 }
206 },
207 Meta::NameValue(named_value) => {
208 let lit = &named_value.lit;
209
210 match lit {
211 Lit::Str(s) => {
212 if name_is_set {
213 panic::reset_parameter(meta_name.as_str());
214 }
215
216 name_is_set = true;
217
218 let s = create_path_string_from_lit_str(s);
219
220 name = match s {
221 Some(s) => TypeAttributeName::Custom(s),
222 None => TypeAttributeName::Disable,
223 };
224 },
225 Lit::Bool(s) => {
226 if name_is_set {
227 panic::reset_parameter(meta_name.as_str());
228 }
229
230 name_is_set = true;
231
232 if s.value {
233 name = TypeAttributeName::Default;
234 } else {
235 name = TypeAttributeName::Disable;
236 }
237 },
238 _ => panic::parameter_incorrect_format(
239 meta_name.as_str(),
240 &correct_usage_for_name,
241 ),
242 }
243 },
244 _ => panic::parameter_incorrect_format(
245 meta_name.as_str(),
246 &correct_usage_for_name,
247 ),
248 }
249 },
250 "named_field" => {
251 if !self.enable_named_field {
252 panic::unknown_parameter("Debug", meta_name.as_str());
253 }
254
255 match meta {
256 Meta::List(list) => {
257 for p in list.nested.iter() {
258 match p {
259 NestedMeta::Lit(Lit::Bool(s)) => {
260 if named_field_is_set {
261 panic::reset_parameter(
262 meta_name.as_str(),
263 );
264 }
265
266 named_field_is_set = true;
267
268 named_field = s.value;
269 },
270 _ => panic::parameter_incorrect_format(
271 meta_name.as_str(),
272 &correct_usage_for_named_field,
273 ),
274 }
275 }
276 },
277 Meta::NameValue(named_value) => {
278 let lit = &named_value.lit;
279
280 match lit {
281 Lit::Bool(s) => {
282 if named_field_is_set {
283 panic::reset_parameter(meta_name.as_str());
284 }
285
286 named_field_is_set = true;
287
288 named_field = s.value;
289 },
290 _ => panic::parameter_incorrect_format(
291 meta_name.as_str(),
292 &correct_usage_for_named_field,
293 ),
294 }
295 },
296 _ => panic::parameter_incorrect_format(
297 meta_name.as_str(),
298 &correct_usage_for_named_field,
299 ),
300 }
301 },
302 "bound" => {
303 if !self.enable_bound {
304 panic::unknown_parameter("Debug", meta_name.as_str());
305 }
306
307 match meta {
308 Meta::List(list) => {
309 for p in list.nested.iter() {
310 match p {
311 NestedMeta::Lit(Lit::Str(s)) => {
312 if bound_is_set {
313 panic::reset_parameter(
314 meta_name.as_str(),
315 );
316 }
317
318 bound_is_set = true;
319
320 let where_predicates =
321 create_where_predicates_from_lit_str(s);
322
323 bound = match where_predicates {
324 Some(where_predicates) => {
325 TypeAttributeBound::Custom(
326 where_predicates,
327 )
328 },
329 None => panic::empty_parameter(
330 meta_name.as_str(),
331 ),
332 };
333 },
334 _ => panic::parameter_incorrect_format(
335 meta_name.as_str(),
336 &correct_usage_for_bound,
337 ),
338 }
339 }
340 },
341 Meta::NameValue(named_value) => {
342 let lit = &named_value.lit;
343
344 match lit {
345 Lit::Str(s) => {
346 if bound_is_set {
347 panic::reset_parameter(meta_name.as_str());
348 }
349
350 bound_is_set = true;
351
352 let where_predicates =
353 create_where_predicates_from_lit_str(s);
354
355 bound = match where_predicates {
356 Some(where_predicates) => {
357 TypeAttributeBound::Custom(
358 where_predicates,
359 )
360 },
361 None => panic::empty_parameter(
362 meta_name.as_str(),
363 ),
364 };
365 },
366 _ => panic::parameter_incorrect_format(
367 meta_name.as_str(),
368 &correct_usage_for_bound,
369 ),
370 }
371 },
372 Meta::Path(_) => {
373 if bound_is_set {
374 panic::reset_parameter(meta_name.as_str());
375 }
376
377 bound_is_set = true;
378
379 bound = TypeAttributeBound::Auto;
380 },
381 }
382 },
383 _ => panic::unknown_parameter("Debug", meta_name.as_str()),
384 }
385 },
386 NestedMeta::Lit(lit) => match lit {
387 Lit::Str(s) => {
388 if !self.enable_name {
389 panic::attribute_incorrect_format(
390 "Debug",
391 &correct_usage_for_debug_attribute,
392 )
393 }
394
395 if name_is_set {
396 panic::reset_parameter("name");
397 }
398
399 name_is_set = true;
400
401 let s = create_path_string_from_lit_str(s);
402
403 name = match s {
404 Some(s) => TypeAttributeName::Custom(s),
405 None => TypeAttributeName::Disable,
406 };
407 },
408 _ => panic::attribute_incorrect_format(
409 "Debug",
410 &correct_usage_for_debug_attribute,
411 ),
412 },
413 }
414 }
415 },
416 Meta::NameValue(named_value) => {
417 let lit = &named_value.lit;
418
419 match lit {
420 Lit::Str(s) => {
421 if !self.enable_name {
422 panic::attribute_incorrect_format(
423 "Debug",
424 &correct_usage_for_debug_attribute,
425 )
426 }
427
428 let s = create_path_string_from_lit_str(s);
429
430 name = match s {
431 Some(s) => TypeAttributeName::Custom(s),
432 None => TypeAttributeName::Disable,
433 };
434 },
435 _ => panic::attribute_incorrect_format(
436 "Debug",
437 &correct_usage_for_debug_attribute,
438 ),
439 }
440 },
441 Meta::Path(_) => {
442 if !self.enable_flag {
443 panic::attribute_incorrect_format("Debug", &correct_usage_for_debug_attribute);
444 }
445
446 flag = true;
447 },
448 }
449
450 TypeAttribute {
451 flag,
452 name,
453 named_field,
454 bound,
455 }
456 }
457
458 #[allow(clippy::wrong_self_convention)]
459 pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> TypeAttribute {
460 let mut result = None;
461
462 for attribute in attributes.iter() {
463 if attribute.path.is_ident("educe") {
464 let meta = attribute.parse_meta().unwrap();
465
466 match meta {
467 Meta::List(list) => {
468 for p in list.nested.iter() {
469 match p {
470 NestedMeta::Meta(meta) => {
471 let meta_name = meta.path().into_token_stream().to_string();
472
473 let t = Trait::from_str(meta_name);
474
475 if traits.binary_search(&t).is_err() {
476 panic::trait_not_used(t);
477 }
478
479 if t == Trait::Debug {
480 if result.is_some() {
481 panic::reuse_a_trait(t);
482 }
483
484 result = Some(self.from_debug_meta(meta));
485 }
486 },
487 _ => panic::educe_format_incorrect(),
488 }
489 }
490 },
491 _ => panic::educe_format_incorrect(),
492 }
493 }
494 }
495
496 result.unwrap_or(TypeAttribute {
497 flag: false,
498 name: self.name,
499 named_field: self.named_field,
500 bound: TypeAttributeBound::None,
501 })
502 }
503}