keyby/
lib.rs

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}