educe/trait_handlers/hash/
hash_struct.rs

1use std::str::FromStr;
2
3use proc_macro2::TokenStream;
4use quote::quote;
5use syn::{Data, DeriveInput, Generics, Meta};
6
7use super::{
8    super::TraitHandler,
9    models::{FieldAttributeBuilder, TypeAttributeBuilder},
10};
11use crate::Trait;
12
13pub struct HashStructHandler;
14
15impl TraitHandler for HashStructHandler {
16    fn trait_meta_handler(
17        ast: &DeriveInput,
18        tokens: &mut TokenStream,
19        traits: &[Trait],
20        meta: &Meta,
21    ) {
22        let type_attribute = TypeAttributeBuilder {
23            enable_flag: true, enable_bound: true
24        }
25        .from_hash_meta(meta);
26
27        let bound = type_attribute
28            .bound
29            .into_punctuated_where_predicates_by_generic_parameters(&ast.generics.params);
30
31        let mut hasher_tokens = TokenStream::new();
32
33        if let Data::Struct(data) = &ast.data {
34            for (index, field) in data.fields.iter().enumerate() {
35                let field_attribute = FieldAttributeBuilder {
36                    enable_ignore: true,
37                    enable_impl:   true,
38                }
39                .from_attributes(&field.attrs, traits);
40
41                if field_attribute.ignore {
42                    continue;
43                }
44
45                let hash_trait = field_attribute.hash_trait;
46                let hash_method = field_attribute.hash_method;
47
48                let field_name = if let Some(ident) = field.ident.as_ref() {
49                    ident.to_string()
50                } else {
51                    format!("{}", index)
52                };
53
54                match hash_trait {
55                    Some(hash_trait) => {
56                        let hash_method = hash_method.unwrap();
57
58                        let statement = format!(
59                            "{hash_trait}::{hash_method}(&self.{field_name}, state);",
60                            hash_trait = hash_trait,
61                            hash_method = hash_method,
62                            field_name = field_name
63                        );
64
65                        hasher_tokens.extend(TokenStream::from_str(&statement).unwrap());
66                    },
67                    None => match hash_method {
68                        Some(hash_method) => {
69                            let statement = format!(
70                                "{hash_method}(&self.{field_name}, state);",
71                                hash_method = hash_method,
72                                field_name = field_name
73                            );
74
75                            hasher_tokens.extend(TokenStream::from_str(&statement).unwrap());
76                        },
77                        None => {
78                            let statement = format!(
79                                "core::hash::Hash::hash(&self.{field_name}, state);",
80                                field_name = field_name
81                            );
82
83                            hasher_tokens.extend(TokenStream::from_str(&statement).unwrap());
84                        },
85                    },
86                }
87            }
88        }
89
90        let ident = &ast.ident;
91
92        let mut generics_cloned: Generics = ast.generics.clone();
93
94        let where_clause = generics_cloned.make_where_clause();
95
96        for where_predicate in bound {
97            where_clause.predicates.push(where_predicate);
98        }
99
100        let (impl_generics, ty_generics, where_clause) = generics_cloned.split_for_impl();
101
102        let hash_impl = quote! {
103            impl #impl_generics core::hash::Hash for #ident #ty_generics #where_clause {
104                #[inline]
105                fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
106                    #hasher_tokens
107                }
108            }
109        };
110
111        tokens.extend(hash_impl);
112    }
113}