1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;
extern crate merkle_light;

use proc_macro::TokenStream;

#[proc_macro_derive(Hashable)]
pub fn derive_hashable(input: TokenStream) -> TokenStream {
    let s = input.to_string();
    let ast = syn::parse_derive_input(&s).unwrap();
    let gen = impl_hashable(&ast);
    gen.parse().unwrap()
}

fn impl_hashable(ast: &syn::DeriveInput) -> quote::Tokens {
    let body = match ast.body {
        syn::Body::Struct(ref s) => s,
        _ => panic!("#[derive(Hashable)] is only defined for structs."),
    };

    let stmts: Vec<_> = match *body {
        syn::VariantData::Struct(ref fields) => {
            fields.iter().enumerate().map(hash_field_map).collect()
        }
        syn::VariantData::Tuple(ref fields) => {
            fields.iter().enumerate().map(hash_field_map).collect()
        }
        syn::VariantData::Unit => panic!("#[derive(Hashable)] is not defined for Unit structs."),
    };

    let name = &ast.ident;
    let dummy_const = syn::Ident::new(format!("_IMPL_HASHABLE_FOR_{}", name).to_uppercase());

    quote! {
        const #dummy_const: () = {
            extern crate merkle_light;

            use std::hash::Hasher;
            use merkle_light::hash::Hashable;

            impl<H: Hasher> Hashable<H> for #name {
                fn hash(&self, state: &mut H) {
                    #(#stmts)*
                }
            }
        };
    }
}

fn hash_field_map(tuple: (usize, &syn::Field)) -> quote::Tokens {
    hash_field(tuple.0, tuple.1)
}

fn hash_field(index: usize, f: &syn::Field) -> quote::Tokens {
    let mut ty = f.ty.clone();

    loop {
        match ty {
            syn::Ty::Path(_, ref path) => {
                path.segments.first().expect(
                    "there must be at least 1 segment",
                );
                break;
            }
            syn::Ty::Rptr(_, bty) => {
                ty = bty.ty.clone();
            }
            _ => panic!(format!("hashing not supported: {:?}", ty)),
        };
    }

    match f.ident {
        Some(ref ident) => quote! { self.#ident.hash(state); },
        None => quote! { self.#index.hash(state); },
    }
}