educe/trait_handlers/hash/
hash_struct.rs

1use quote::quote;
2use syn::{Data, DeriveInput, Meta, Path, Type};
3
4use super::{
5    models::{FieldAttributeBuilder, TypeAttributeBuilder},
6    TraitHandler,
7};
8use crate::{common::ident_index::IdentOrIndex, Trait};
9
10pub(crate) struct HashStructHandler;
11
12impl TraitHandler for HashStructHandler {
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        if let Data::Struct(data) = &ast.data {
31            let built_in_hash: Path = syn::parse2(quote!(::core::hash::Hash::hash)).unwrap();
32
33            for (index, field) in data.fields.iter().enumerate() {
34                let field_attribute = FieldAttributeBuilder {
35                    enable_ignore: true,
36                    enable_method: true,
37                }
38                .build_from_attributes(&field.attrs, traits)?;
39
40                if field_attribute.ignore {
41                    continue;
42                }
43
44                let field_name = if let Some(ident) = field.ident.as_ref() {
45                    IdentOrIndex::from(ident)
46                } else {
47                    IdentOrIndex::from(index)
48                };
49
50                let hash = field_attribute.method.as_ref().unwrap_or_else(|| {
51                    hash_types.push(&field.ty);
52                    &built_in_hash
53                });
54
55                hash_token_stream.extend(quote!( #hash(&self.#field_name, state); ));
56            }
57        }
58
59        let ident = &ast.ident;
60
61        let bound = type_attribute.bound.into_where_predicates_by_generic_parameters_check_types(
62            &ast.generics.params,
63            &syn::parse2(quote!(::core::hash::Hash)).unwrap(),
64            &hash_types,
65            &[],
66        );
67
68        let mut generics = ast.generics.clone();
69        let where_clause = generics.make_where_clause();
70
71        for where_predicate in bound {
72            where_clause.predicates.push(where_predicate);
73        }
74
75        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
76
77        token_stream.extend(quote! {
78            impl #impl_generics ::core::hash::Hash for #ident #ty_generics #where_clause {
79                #[inline]
80                fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
81                    #hash_token_stream
82                }
83            }
84        });
85
86        Ok(())
87    }
88}