1extern crate proc_macro;
2extern crate proc_macro2;
3#[macro_use]
4extern crate quote;
5#[macro_use]
6extern crate syn;
7
8use proc_macro::TokenStream;
9use proc_macro2::{TokenStream as TokenStream2, Literal as Literal2};
10use syn::{Data, DeriveInput, Fields, GenericParam, Generics};
11
12#[proc_macro_derive(Hash32)]
13pub fn derive_hash32(input: TokenStream) -> TokenStream {
14 let input: DeriveInput = syn::parse(input).unwrap();
15
16 let name = input.ident;
17 let generics = add_trait_bounds(input.generics);
18 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
19
20 let const_ = format_ident!("__IMPL_HASH32_FOR_{}", name);
21 let hash = compute_hash(&input.data);
22 quote!(
23 #[allow(non_upper_case_globals)]
24 const #const_: () = {
25 extern crate hash32;
26
27 impl #impl_generics hash32::Hash for #name #ty_generics #where_clause {
28 fn hash<H: hash32::Hasher>(&self, _h: &mut H) -> () {
29 #hash
30 }
31 }
32 };
33 )
34 .into()
35}
36
37fn add_trait_bounds(mut generics: Generics) -> Generics {
39 for param in &mut generics.params {
40 if let GenericParam::Type(ref mut type_param) = *param {
41 type_param.bounds.push(parse_quote!(hash32::Hash));
42 }
43 }
44 generics
45}
46
47fn compute_hash(data: &Data) -> TokenStream2 {
48 match *data {
49 Data::Struct(ref data) => match data.fields {
50 Fields::Named(ref fields) => {
51 let fnames = fields.named.iter().map(|f| &f.ident);
52 quote! {
53 #(
54 hash32::Hash::hash(&self.#fnames, _h);
55 )*
56 }
57 }
58 Fields::Unnamed(ref fields) => {
59 let indices = (0..fields.unnamed.len()).map(|index| Literal2::usize_unsuffixed(index));
60 quote! {
61 #(
62 hash32::Hash::hash(&self.#indices, _h);
63 )*
64 }
65 }
66 Fields::Unit => quote! {},
67 },
68 Data::Enum(..) | Data::Union(..) => {
69 panic!("#[derive(Hash)] doesn't currently support `enum` and `union`")
70 }
71 }
72}