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 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
use proc_macro::TokenStream; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::quote; use syn::{parse_macro_input, Data, DeriveInput, Error}; /// Deriving `DiscriminantHash` implements [`Hash`] trait /// for the underlying enum. Here hash is only dependent on discriminant /// and isn't effected by variant's fields. /// /// [`Hash`]: std::hash::Hash /// /// # Example /// /// ``` /// use discriminant_hash_derive::DiscriminantHash; /// use std::{ /// collections::hash_map::DefaultHasher, /// hash::{Hash, Hasher}, /// }; /// /// #[derive(DiscriminantHash)] /// enum Abc<T> { /// Simple, /// HashNotImplemented(Xyz), /// Generic(T), /// } /// /// #[allow(unused)] /// #[derive(Hash)] /// enum Pqr<'a> { /// Simple, /// Lifetime(&'a str), /// } /// /// // Xyz doesn't impl Hash /// struct Xyz; /// /// fn main() { /// assert_eq!(my_hash(Abc::Simple::<i32>), my_hash(Abc::Simple::<Xyz>)); /// assert_eq!( /// my_hash(Abc::HashNotImplemented::<i32>(Xyz)), /// my_hash(Abc::HashNotImplemented::<String>(Xyz)) /// ); /// assert_eq!( /// my_hash(Abc::Generic::<i32>(4)), /// my_hash(Abc::Generic::<Xyz>(Xyz)) /// ); /// /// assert_ne!( /// my_hash(Abc::Simple::<i32>), /// my_hash(Abc::Generic::<Xyz>(Xyz)) /// ); /// /// /// // This may be same depending on how Pqr is defined /// // assert_eq!( /// // my_hash(Abc::Simple::<i32>), /// // my_hash(Pqr::Simple) /// // ); /// /// } /// /// fn my_hash<T>(obj: T) -> u64 /// where /// T: Hash, /// { /// let mut hasher = DefaultHasher::new(); /// obj.hash(&mut hasher); /// hasher.finish() /// } /// ``` #[proc_macro_derive(DiscriminantHash)] pub fn discriminant_hash_derive(input: TokenStream) -> TokenStream { let derive_input = parse_macro_input!(input as DeriveInput); impl_hash(&derive_input).into() } fn impl_hash(derive_input: &DeriveInput) -> TokenStream2 { // TODO attribute for having specific variant's hash include its value let _data_enum = if let Data::Enum(ref data_enum) = derive_input.data { data_enum } else { return Error::new( Span::call_site(), "#[derive(DiscriminantHash)] only works with enum", ) .to_compile_error(); }; let enum_name = &derive_input.ident; let (impl_generics, ty_generics, where_clause) = derive_input.generics.split_for_impl(); quote! { impl #impl_generics std::hash::Hash for #enum_name #ty_generics #where_clause { fn hash<H: std::hash::Hasher>(&self, state: &mut H) { std::hash::Hash::hash( &std::mem::discriminant(self), state ) } } } }