1extern crate proc_macro;
2extern crate syn;
3#[macro_use]
4extern crate quote;
5
6use proc_macro::TokenStream;
7use proc_macro2::{Ident, Span};
8use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Fields};
9
10#[proc_macro_attribute]
11pub fn key_by(key: TokenStream, input: TokenStream) -> TokenStream {
12 let item = parse_macro_input!(input as DeriveInput);
13 let name = &item.ident;
14
15 let _fields = match &item.data {
16 Data::Struct(DataStruct {
17 fields: Fields::Named(fields),
18 ..
19 }) => &fields.named,
20 _ => panic!("expected a struct with named fields"),
21 };
22
23 let keys: Vec<proc_macro2::TokenStream> = key
24 .to_string()
25 .trim()
26 .split(",")
27 .map(|k| {
28 let struct_field = Ident::new(&k.trim(), Span::call_site());
29 quote! { self.#struct_field.hash(state); }
30 })
31 .collect();
32
33 let output: proc_macro2::TokenStream = {
34 quote! {
35 #item
36 impl ::std::hash::Hash for #name {
37 fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
38 #(#keys)*
39 }
40 }
41 }
42 };
43
44 proc_macro::TokenStream::from(output)
45}