educe/trait_handlers/eq/models/
type_attribute.rs
1use syn::{punctuated::Punctuated, Attribute, Meta, Token};
2
3use crate::{common::bound::Bound, panic, Trait};
4
5pub(crate) struct TypeAttribute {
6 pub(crate) bound: Bound,
7}
8
9#[derive(Debug)]
10pub(crate) struct TypeAttributeBuilder {
11 pub(crate) enable_flag: bool,
12 pub(crate) enable_bound: bool,
13}
14
15impl TypeAttributeBuilder {
16 pub(crate) fn build_from_eq_meta(&self, meta: &Meta) -> syn::Result<TypeAttribute> {
17 debug_assert!(meta.path().is_ident("Eq"));
18
19 let mut bound = Bound::Auto;
20
21 let correct_usage_for_copy_attribute = {
22 let mut usage = vec![];
23
24 if self.enable_flag {
25 usage.push(stringify!(#[educe(Eq)]));
26 }
27
28 if self.enable_bound {
29 usage.push(stringify!(#[educe(Eq(bound(where_predicates)))]));
30 usage.push(stringify!(#[educe(Eq(bound = false))]));
31 }
32
33 usage
34 };
35
36 match meta {
37 Meta::Path(_) => {
38 if !self.enable_flag {
39 return Err(panic::attribute_incorrect_format(
40 meta.path().get_ident().unwrap(),
41 &correct_usage_for_copy_attribute,
42 ));
43 }
44 },
45 Meta::NameValue(_) => {
46 return Err(panic::attribute_incorrect_format(
47 meta.path().get_ident().unwrap(),
48 &correct_usage_for_copy_attribute,
49 ));
50 },
51 Meta::List(list) => {
52 let result =
53 list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
54
55 let mut bound_is_set = false;
56
57 let mut handler = |meta: Meta| -> syn::Result<bool> {
58 if let Some(ident) = meta.path().get_ident() {
59 if ident == "bound" {
60 if !self.enable_bound {
61 return Ok(false);
62 }
63
64 let v = Bound::from_meta(&meta)?;
65
66 if bound_is_set {
67 return Err(panic::parameter_reset(ident));
68 }
69
70 bound_is_set = true;
71
72 bound = v;
73
74 return Ok(true);
75 }
76 }
77
78 Ok(false)
79 };
80
81 for p in result {
82 if !handler(p)? {
83 return Err(panic::attribute_incorrect_format(
84 meta.path().get_ident().unwrap(),
85 &correct_usage_for_copy_attribute,
86 ));
87 }
88 }
89 },
90 }
91
92 Ok(TypeAttribute {
93 bound,
94 })
95 }
96
97 pub(crate) fn build_from_attributes(
98 &self,
99 attributes: &[Attribute],
100 traits: &[Trait],
101 ) -> syn::Result<TypeAttribute> {
102 let mut output = None;
103
104 for attribute in attributes.iter() {
105 let path = attribute.path();
106
107 if path.is_ident("educe") {
108 if let Meta::List(list) = &attribute.meta {
109 let result =
110 list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
111
112 for meta in result {
113 let path = meta.path();
114
115 let t = match Trait::from_path(path) {
116 Some(t) => t,
117 None => return Err(panic::unsupported_trait(meta.path())),
118 };
119
120 if !traits.contains(&t) {
121 return Err(panic::trait_not_used(path.get_ident().unwrap()));
122 }
123
124 if t == Trait::Eq {
125 if output.is_some() {
126 return Err(panic::reuse_a_trait(path.get_ident().unwrap()));
127 }
128
129 output = Some(self.build_from_eq_meta(&meta)?);
130 }
131 }
132 }
133 }
134 }
135
136 Ok(output.unwrap_or(TypeAttribute {
137 bound: Bound::Auto
138 }))
139 }
140}