educe/trait_handlers/hash/
hash_enum.rs1use quote::{format_ident, quote};
2use syn::{Data, DeriveInput, Fields, Meta, Path, Type};
3
4use super::{
5 models::{FieldAttributeBuilder, TypeAttributeBuilder},
6 TraitHandler,
7};
8use crate::Trait;
9
10pub(crate) struct HashEnumHandler;
11
12impl TraitHandler for HashEnumHandler {
13 #[inline]
14 fn trait_meta_handler(
15 ast: &DeriveInput,
16 token_stream: &mut proc_macro2::TokenStream,
17 traits: &[Trait],
18 meta: &Meta,
19 ) -> syn::Result<()> {
20 let type_attribute =
21 TypeAttributeBuilder {
22 enable_flag: true, enable_unsafe: false, enable_bound: true
23 }
24 .build_from_hash_meta(meta)?;
25
26 let mut hash_types: Vec<&Type> = Vec::new();
27
28 let mut hash_token_stream = proc_macro2::TokenStream::new();
29
30 let mut arms_token_stream = proc_macro2::TokenStream::new();
31
32 if let Data::Enum(data) = &ast.data {
33 for (variant_index, variant) in data.variants.iter().enumerate() {
34 let _ = TypeAttributeBuilder {
35 enable_flag: false,
36 enable_unsafe: false,
37 enable_bound: false,
38 }
39 .build_from_attributes(&variant.attrs, traits)?;
40
41 let variant_ident = &variant.ident;
42
43 let built_in_hash: Path = syn::parse2(quote!(::core::hash::Hash::hash)).unwrap();
44
45 match &variant.fields {
46 Fields::Unit => {
47 arms_token_stream.extend(quote! {
48 Self::#variant_ident => {
49 ::core::hash::Hash::hash(&#variant_index, state);
50 }
51 });
52 },
53 Fields::Named(_) => {
54 let mut pattern_token_stream = proc_macro2::TokenStream::new();
55 let mut block_token_stream = proc_macro2::TokenStream::new();
56
57 for field in variant.fields.iter() {
58 let field_attribute = FieldAttributeBuilder {
59 enable_ignore: true,
60 enable_method: true,
61 }
62 .build_from_attributes(&field.attrs, traits)?;
63
64 let field_name_real = field.ident.as_ref().unwrap();
65 let field_name_var = format_ident!("v_{}", field_name_real);
66
67 if field_attribute.ignore {
68 pattern_token_stream.extend(quote!(#field_name_real: _,));
69
70 continue;
71 }
72
73 pattern_token_stream.extend(quote!(#field_name_real: #field_name_var,));
74
75 let hash = field_attribute.method.as_ref().unwrap_or_else(|| {
76 hash_types.push(&field.ty);
77 &built_in_hash
78 });
79
80 block_token_stream.extend(quote!( #hash(#field_name_var, state); ));
81 }
82
83 arms_token_stream.extend(quote! {
84 Self::#variant_ident { #pattern_token_stream } => {
85 ::core::hash::Hash::hash(&#variant_index, state);
86
87 #block_token_stream
88 }
89 });
90 },
91 Fields::Unnamed(_) => {
92 let mut pattern_token_stream = proc_macro2::TokenStream::new();
93 let mut block_token_stream = proc_macro2::TokenStream::new();
94
95 for (index, field) in variant.fields.iter().enumerate() {
96 let field_attribute = FieldAttributeBuilder {
97 enable_ignore: true,
98 enable_method: true,
99 }
100 .build_from_attributes(&field.attrs, traits)?;
101
102 let field_name_var = format_ident!("_{}", index);
103
104 if field_attribute.ignore {
105 pattern_token_stream.extend(quote!(_,));
106
107 continue;
108 }
109
110 pattern_token_stream.extend(quote!(#field_name_var,));
111
112 let hash = field_attribute.method.as_ref().unwrap_or_else(|| {
113 hash_types.push(&field.ty);
114 &built_in_hash
115 });
116
117 block_token_stream.extend(quote!( #hash(#field_name_var, state); ));
118 }
119
120 arms_token_stream.extend(quote! {
121 Self::#variant_ident ( #pattern_token_stream ) => {
122 ::core::hash::Hash::hash(&#variant_index, state);
123
124 #block_token_stream
125 }
126 });
127 },
128 }
129 }
130 }
131
132 if !arms_token_stream.is_empty() {
133 hash_token_stream.extend(quote! {
134 match self {
135 #arms_token_stream
136 }
137 });
138 }
139
140 let ident = &ast.ident;
141
142 let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
143 &ast.generics.params,
144 &syn::parse2(quote!(::core::hash::Hash)).unwrap(),
145 &hash_types,
146 &[],
147 );
148
149 let mut generics = ast.generics.clone();
150 let where_clause = generics.make_where_clause();
151
152 for where_predicate in bound {
153 where_clause.predicates.push(where_predicate);
154 }
155
156 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
157
158 token_stream.extend(quote! {
159 impl #impl_generics ::core::hash::Hash for #ident #ty_generics #where_clause {
160 #[inline]
161 fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
162 #hash_token_stream
163 }
164 }
165 });
166
167 Ok(())
168 }
169}