ai generated
This commit is contained in:
12
hash2_derive/Cargo.toml
Normal file
12
hash2_derive/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "hash2_derive"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "2", features = ["full"] }
|
||||
quote = "1"
|
||||
proc-macro2 = "1"
|
||||
126
hash2_derive/src/lib.rs
Normal file
126
hash2_derive/src/lib.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, Data, DeriveInput, Fields, GenericParam, Generics};
|
||||
|
||||
#[proc_macro_derive(Hash2)]
|
||||
pub fn derive_hash2(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let name = &input.ident;
|
||||
let generics = &input.generics;
|
||||
|
||||
// Extract field hashing logic
|
||||
let hash_fields = match &input.data {
|
||||
Data::Struct(data) => {
|
||||
match &data.fields {
|
||||
Fields::Named(fields) => {
|
||||
let field_names = fields.named.iter().map(|f| &f.ident);
|
||||
quote! {
|
||||
#(
|
||||
::hash2::Hash2::hash(&self.#field_names, state);
|
||||
)*
|
||||
}
|
||||
}
|
||||
Fields::Unnamed(fields) => {
|
||||
let field_indices = (0..fields.unnamed.len()).map(syn::Index::from);
|
||||
quote! {
|
||||
#(
|
||||
::hash2::Hash2::hash(&self.#field_indices, state);
|
||||
)*
|
||||
}
|
||||
}
|
||||
Fields::Unit => {
|
||||
quote! {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Data::Enum(data) => {
|
||||
let variants = data.variants.iter().map(|variant| {
|
||||
let variant_name = &variant.ident;
|
||||
match &variant.fields {
|
||||
Fields::Named(fields) => {
|
||||
let field_names: Vec<_> = fields.named.iter().map(|f| &f.ident).collect();
|
||||
let field_names2 = field_names.clone();
|
||||
quote! {
|
||||
#name::#variant_name { #(#field_names),* } => {
|
||||
#(
|
||||
::hash2::Hash2::hash(#field_names2, state);
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
Fields::Unnamed(fields) => {
|
||||
let field_names: Vec<_> = (0..fields.unnamed.len())
|
||||
.map(|i| syn::Ident::new(&format!("f{}", i), proc_macro2::Span::call_site()))
|
||||
.collect();
|
||||
let field_names2 = field_names.clone();
|
||||
quote! {
|
||||
#name::#variant_name(#(#field_names),*) => {
|
||||
#(
|
||||
::hash2::Hash2::hash(#field_names2, state);
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
Fields::Unit => {
|
||||
quote! {
|
||||
#name::#variant_name => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
match self {
|
||||
#(#variants)*
|
||||
}
|
||||
}
|
||||
}
|
||||
Data::Union(_) => {
|
||||
panic!("Hash2 cannot be derived for unions");
|
||||
}
|
||||
};
|
||||
|
||||
// Build where clause with Hash2 bounds for generic types
|
||||
let where_clause = build_where_clause(generics);
|
||||
|
||||
// Split generics for impl
|
||||
let (impl_generics, ty_generics, _) = generics.split_for_impl();
|
||||
|
||||
let expanded = quote! {
|
||||
impl #impl_generics ::hash2::Hash2 for #name #ty_generics #where_clause {
|
||||
fn hash<H: ::hash2::Hasher>(&self, state: &mut H) {
|
||||
#hash_fields
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TokenStream::from(expanded)
|
||||
}
|
||||
|
||||
fn build_where_clause(generics: &Generics) -> proc_macro2::TokenStream {
|
||||
let mut predicates = Vec::new();
|
||||
|
||||
// Add Hash2 bound for each type parameter (but not lifetimes)
|
||||
for param in &generics.params {
|
||||
if let GenericParam::Type(type_param) = param {
|
||||
let ident = &type_param.ident;
|
||||
predicates.push(quote! { #ident: ::hash2::Hash2 });
|
||||
}
|
||||
}
|
||||
|
||||
// Include existing where predicates
|
||||
if let Some(where_clause) = &generics.where_clause {
|
||||
for predicate in &where_clause.predicates {
|
||||
predicates.push(quote! { #predicate });
|
||||
}
|
||||
}
|
||||
|
||||
if predicates.is_empty() {
|
||||
quote! {}
|
||||
} else {
|
||||
quote! {
|
||||
where #(#predicates),*
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user