merkle_light_derive/
lib.rs

1extern crate proc_macro;
2extern crate syn;
3#[macro_use]
4extern crate quote;
5extern crate merkle_light;
6
7use proc_macro::TokenStream;
8
9#[proc_macro_derive(Hashable)]
10pub fn derive_hashable(input: TokenStream) -> TokenStream {
11    let s = input.to_string();
12    let ast = syn::parse_derive_input(&s).unwrap();
13    let gen = impl_hashable(&ast);
14    gen.parse().unwrap()
15}
16
17fn impl_hashable(ast: &syn::DeriveInput) -> quote::Tokens {
18    let body = match ast.body {
19        syn::Body::Struct(ref s) => s,
20        _ => panic!("#[derive(Hashable)] is only defined for structs."),
21    };
22
23    let stmts: Vec<_> = match *body {
24        syn::VariantData::Struct(ref fields) => {
25            fields.iter().enumerate().map(hash_field_map).collect()
26        }
27        syn::VariantData::Tuple(ref fields) => {
28            fields.iter().enumerate().map(hash_field_map).collect()
29        }
30        syn::VariantData::Unit => panic!("#[derive(Hashable)] is not defined for Unit structs."),
31    };
32
33    let name = &ast.ident;
34    let dummy_const = syn::Ident::new(format!("_IMPL_HASHABLE_FOR_{}", name).to_uppercase());
35
36    quote! {
37        const #dummy_const: () = {
38            extern crate merkle_light;
39
40            use std::hash::Hasher;
41            use merkle_light::hash::Hashable;
42
43            impl<H: Hasher> Hashable<H> for #name {
44                fn hash(&self, state: &mut H) {
45                    #(#stmts)*
46                }
47            }
48        };
49    }
50}
51
52fn hash_field_map(tuple: (usize, &syn::Field)) -> quote::Tokens {
53    hash_field(tuple.0, tuple.1)
54}
55
56fn hash_field(index: usize, f: &syn::Field) -> quote::Tokens {
57    let mut ty = f.ty.clone();
58
59    loop {
60        match ty {
61            syn::Ty::Path(_, ref path) => {
62                path.segments.first().expect(
63                    "there must be at least 1 segment",
64                );
65                break;
66            }
67            syn::Ty::Rptr(_, bty) => {
68                ty = bty.ty.clone();
69            }
70            _ => panic!("hashing not supported: {:?}", ty),
71        };
72    }
73
74    match f.ident {
75        Some(ref ident) => quote! { self.#ident.hash(state); },
76        None => quote! { self.#index.hash(state); },
77    }
78}