educe/trait_handlers/hash/models/type_attribute.rs
1use quote::{quote, ToTokens};
2use syn::{
3 punctuated::Punctuated, token::Comma, Attribute, GenericParam, Lit, Meta, NestedMeta,
4 WherePredicate,
5};
6
7use super::super::super::{
8 create_where_predicates_from_generic_parameters, create_where_predicates_from_lit_str,
9};
10use crate::{panic, Trait};
11
12#[derive(Clone)]
13pub enum TypeAttributeBound {
14 None,
15 Auto,
16 Custom(Punctuated<WherePredicate, Comma>),
17}
18
19impl TypeAttributeBound {
20 pub fn into_punctuated_where_predicates_by_generic_parameters(
21 self,
22 params: &Punctuated<GenericParam, Comma>,
23 ) -> Punctuated<WherePredicate, Comma> {
24 match self {
25 TypeAttributeBound::None => Punctuated::new(),
26 TypeAttributeBound::Auto => create_where_predicates_from_generic_parameters(
27 params,
28 &syn::parse2(quote!(core::hash::Hash)).unwrap(),
29 ),
30 TypeAttributeBound::Custom(where_predicates) => where_predicates,
31 }
32 }
33}
34
35#[derive(Clone)]
36pub struct TypeAttribute {
37 pub flag: bool,
38 pub bound: TypeAttributeBound,
39}
40
41#[derive(Debug, Clone)]
42pub struct TypeAttributeBuilder {
43 pub enable_flag: bool,
44 pub enable_bound: bool,
45}
46
47impl TypeAttributeBuilder {
48 #[allow(clippy::wrong_self_convention)]
49 pub fn from_hash_meta(&self, meta: &Meta) -> TypeAttribute {
50 let mut flag = false;
51 let mut bound = TypeAttributeBound::None;
52
53 let correct_usage_for_hash_attribute = {
54 let mut usage = vec![];
55
56 if self.enable_flag {
57 usage.push(stringify!(#[educe(Hash)]));
58 }
59
60 usage
61 };
62
63 let correct_usage_for_bound = {
64 let usage = vec![
65 stringify!(#[educe(Hash(bound))]),
66 stringify!(#[educe(Hash(bound = "where_predicates"))]),
67 stringify!(#[educe(Hash(bound("where_predicates")))]),
68 ];
69
70 usage
71 };
72
73 match meta {
74 Meta::List(list) => {
75 let mut bound_is_set = false;
76
77 for p in list.nested.iter() {
78 match p {
79 NestedMeta::Meta(meta) => {
80 let meta_name = meta.path().into_token_stream().to_string();
81
82 match meta_name.as_str() {
83 "bound" => {
84 if !self.enable_bound {
85 panic::unknown_parameter("Hash", meta_name.as_str());
86 }
87
88 match meta {
89 Meta::List(list) => {
90 for p in list.nested.iter() {
91 match p {
92 NestedMeta::Lit(Lit::Str(s)) => {
93 if bound_is_set {
94 panic::reset_parameter(
95 meta_name.as_str(),
96 );
97 }
98
99 bound_is_set = true;
100
101 let where_predicates =
102 create_where_predicates_from_lit_str(s);
103
104 bound = match where_predicates {
105 Some(where_predicates) => {
106 TypeAttributeBound::Custom(
107 where_predicates,
108 )
109 },
110 None => panic::empty_parameter(
111 meta_name.as_str(),
112 ),
113 };
114 },
115 _ => panic::parameter_incorrect_format(
116 meta_name.as_str(),
117 &correct_usage_for_bound,
118 ),
119 }
120 }
121 },
122 Meta::NameValue(named_value) => {
123 let lit = &named_value.lit;
124
125 match lit {
126 Lit::Str(s) => {
127 if bound_is_set {
128 panic::reset_parameter(meta_name.as_str());
129 }
130
131 bound_is_set = true;
132
133 let where_predicates =
134 create_where_predicates_from_lit_str(s);
135
136 bound = match where_predicates {
137 Some(where_predicates) => {
138 TypeAttributeBound::Custom(
139 where_predicates,
140 )
141 },
142 None => panic::empty_parameter(
143 meta_name.as_str(),
144 ),
145 };
146 },
147 _ => panic::parameter_incorrect_format(
148 meta_name.as_str(),
149 &correct_usage_for_bound,
150 ),
151 }
152 },
153 Meta::Path(_) => {
154 if bound_is_set {
155 panic::reset_parameter(meta_name.as_str());
156 }
157
158 bound_is_set = true;
159
160 bound = TypeAttributeBound::Auto;
161 },
162 }
163 },
164 _ => panic::unknown_parameter("Hash", meta_name.as_str()),
165 }
166 },
167 _ => panic::attribute_incorrect_format(
168 "Hash",
169 &correct_usage_for_hash_attribute,
170 ),
171 }
172 }
173 },
174 Meta::NameValue(_) => {
175 panic::attribute_incorrect_format("Hash", &correct_usage_for_hash_attribute)
176 },
177 Meta::Path(_) => {
178 if !self.enable_flag {
179 panic::attribute_incorrect_format("Hash", &correct_usage_for_hash_attribute);
180 }
181
182 flag = true;
183 },
184 }
185
186 TypeAttribute {
187 flag,
188 bound,
189 }
190 }
191
192 #[allow(clippy::wrong_self_convention)]
193 pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> TypeAttribute {
194 let mut result = None;
195
196 for attribute in attributes.iter() {
197 if attribute.path.is_ident("educe") {
198 let meta = attribute.parse_meta().unwrap();
199
200 match meta {
201 Meta::List(list) => {
202 for p in list.nested.iter() {
203 match p {
204 NestedMeta::Meta(meta) => {
205 let meta_name = meta.path().into_token_stream().to_string();
206
207 let t = Trait::from_str(meta_name);
208
209 if traits.binary_search(&t).is_err() {
210 panic::trait_not_used(t);
211 }
212
213 if t == Trait::Hash {
214 if result.is_some() {
215 panic::reuse_a_trait(t);
216 }
217
218 result = Some(self.from_hash_meta(meta));
219 }
220 },
221 _ => panic::educe_format_incorrect(),
222 }
223 }
224 },
225 _ => panic::educe_format_incorrect(),
226 }
227 }
228 }
229
230 result.unwrap_or(TypeAttribute {
231 flag: false, bound: TypeAttributeBound::None
232 })
233 }
234}