ai generated
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/target
|
||||
.idea
|
||||
169
Cargo.lock
generated
Normal file
169
Cargo.lock
generated
Normal file
@@ -0,0 +1,169 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "hash2"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hash2_derive",
|
||||
"ordered-float",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash2_derive"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "5.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "hash2"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[features]
|
||||
|
||||
[dependencies]
|
||||
ordered-float = "5.1.0"
|
||||
hash2_derive = { path = "hash2_derive" }
|
||||
|
||||
uuid = { version = "*", optional = true }
|
||||
98
examples/basic_usage.rs
Normal file
98
examples/basic_usage.rs
Normal file
@@ -0,0 +1,98 @@
|
||||
use hash2::Hash2;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::Hasher;
|
||||
|
||||
// Basic struct with derive
|
||||
#[derive(Hash2)]
|
||||
pub struct User {
|
||||
pub id: u64,
|
||||
pub name: String,
|
||||
pub age: u8,
|
||||
}
|
||||
|
||||
// Struct with generics
|
||||
#[derive(Hash2)]
|
||||
pub struct Container<T> {
|
||||
pub value: T,
|
||||
pub count: usize,
|
||||
}
|
||||
|
||||
// Struct with lifetimes
|
||||
#[derive(Hash2)]
|
||||
pub struct Borrowed<'a> {
|
||||
pub data: &'a str,
|
||||
pub id: u64,
|
||||
}
|
||||
|
||||
// Struct with both generics and lifetimes
|
||||
#[derive(Hash2)]
|
||||
pub struct Complex<'a, T> {
|
||||
pub owned: T,
|
||||
pub borrowed: &'a str,
|
||||
}
|
||||
|
||||
// Example with PhantomData (like the original Test struct)
|
||||
#[derive(Hash2)]
|
||||
pub struct Test<'a> {
|
||||
pub test: f32,
|
||||
pub test2: u64,
|
||||
pub _ignored: std::marker::PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
// Enum example
|
||||
#[derive(Hash2)]
|
||||
pub enum Status {
|
||||
Active,
|
||||
Inactive,
|
||||
Pending { since: u64 },
|
||||
}
|
||||
|
||||
fn compute_hash<T: Hash2>(value: &T) -> u64 {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
value.hash(&mut hasher);
|
||||
hasher.finish()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Basic struct
|
||||
let user = User {
|
||||
id: 42,
|
||||
name: "Alice".to_string(),
|
||||
age: 30,
|
||||
};
|
||||
println!("User hash: {}", compute_hash(&user));
|
||||
|
||||
// Generic struct
|
||||
let container = Container {
|
||||
value: 100u32,
|
||||
count: 5,
|
||||
};
|
||||
println!("Container hash: {}", compute_hash(&container));
|
||||
|
||||
// Borrowed struct
|
||||
let text = String::from("Hello");
|
||||
let borrowed = Borrowed {
|
||||
data: &text,
|
||||
id: 1,
|
||||
};
|
||||
println!("Borrowed hash: {}", compute_hash(&borrowed));
|
||||
|
||||
// Complex struct
|
||||
let complex = Complex {
|
||||
owned: vec![1, 2, 3],
|
||||
borrowed: &text,
|
||||
};
|
||||
println!("Complex hash: {}", compute_hash(&complex));
|
||||
|
||||
// Test struct (original example)
|
||||
let test = Test {
|
||||
test: 3.14,
|
||||
test2: 999,
|
||||
_ignored: std::marker::PhantomData,
|
||||
};
|
||||
println!("Test hash: {}", compute_hash(&test));
|
||||
|
||||
// Enum
|
||||
let status = Status::Pending { since: 12345 };
|
||||
println!("Status hash: {}", compute_hash(&status));
|
||||
}
|
||||
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),*
|
||||
}
|
||||
}
|
||||
}
|
||||
218
src/lib.rs
Normal file
218
src/lib.rs
Normal file
@@ -0,0 +1,218 @@
|
||||
pub use core::hash::Hasher;
|
||||
pub use hash2_derive::Hash2;
|
||||
|
||||
use std::hash::Hash;
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
pub trait Hash2 {
|
||||
fn hash<H: Hasher>(&self, state: &mut H);
|
||||
}
|
||||
|
||||
#[cfg(feature = "uuid")]
|
||||
impl Hash2 for uuid::Uuid {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
let (a,b) = self.as_u64_pair();
|
||||
state.write_u64(a);
|
||||
state.write_u64(b);
|
||||
}
|
||||
}
|
||||
|
||||
// Unsigned integer implementations
|
||||
impl Hash2 for u8 {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_u8(*self);
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash2 for u16 {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_u16(*self);
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash2 for u32 {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_u32(*self);
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash2 for u64 {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_u64(*self);
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash2 for u128 {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_u128(*self);
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash2 for usize {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_usize(*self);
|
||||
}
|
||||
}
|
||||
|
||||
// Signed integer implementations
|
||||
impl Hash2 for i8 {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_i8(*self);
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash2 for i16 {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_i16(*self);
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash2 for i32 {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_i32(*self);
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash2 for i64 {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_i64(*self);
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash2 for i128 {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_i128(*self);
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash2 for isize {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_isize(*self);
|
||||
}
|
||||
}
|
||||
|
||||
// floats
|
||||
impl Hash2 for f32 {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
OrderedFloat(*self).hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash2 for f64 {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
OrderedFloat(*self).hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
// String types
|
||||
impl Hash2 for str {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write(self.as_bytes());
|
||||
state.write_usize(self.len());
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash2 for String {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
Hash2::hash(self.as_str(), state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash2 + ?Sized> Hash2 for &T {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
(*self).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash2 + ?Sized> Hash2 for &mut T {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
(**self).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash2> Hash2 for [T] {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_usize(self.len());
|
||||
for item in self {
|
||||
item.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash2, const N: usize> Hash2 for [T; N] {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
for item in self {
|
||||
item.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash2> Hash2 for Vec<T> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.as_slice().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash2> Hash2 for Option<T> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
None => state.write_u8(0),
|
||||
Some(value) => {
|
||||
state.write_u8(1);
|
||||
value.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Hash2 for bool {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_u8(*self as u8);
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash2 for char {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_u32(*self as u32);
|
||||
}
|
||||
}
|
||||
|
||||
// Tuple implementations
|
||||
impl<T: ?Sized> Hash2 for std::marker::PhantomData<T> {
|
||||
fn hash<H: Hasher>(&self, _state: &mut H) {}
|
||||
}
|
||||
|
||||
impl Hash2 for () {
|
||||
fn hash<H: Hasher>(&self, _state: &mut H) {}
|
||||
}
|
||||
|
||||
impl<T: Hash2> Hash2 for (T,) {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash2, U: Hash2> Hash2 for (T, U) {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
self.1.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash2, U: Hash2, V: Hash2> Hash2 for (T, U, V) {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
self.1.hash(state);
|
||||
self.2.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash2, U: Hash2, V: Hash2, W: Hash2> Hash2 for (T, U, V, W) {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
self.1.hash(state);
|
||||
self.2.hash(state);
|
||||
self.3.hash(state);
|
||||
}
|
||||
}
|
||||
164
tests/derive_test.rs
Normal file
164
tests/derive_test.rs
Normal file
@@ -0,0 +1,164 @@
|
||||
use hash2::Hash2;
|
||||
|
||||
#[derive(Hash2)]
|
||||
struct SimpleStruct {
|
||||
a: u32,
|
||||
b: u64,
|
||||
}
|
||||
|
||||
#[derive(Hash2)]
|
||||
struct GenericStruct<T> {
|
||||
value: T,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
#[derive(Hash2)]
|
||||
struct LifetimeStruct<'a> {
|
||||
name: &'a str,
|
||||
id: u64,
|
||||
}
|
||||
|
||||
#[derive(Hash2)]
|
||||
struct BothGenerics<'a, T> {
|
||||
data: T,
|
||||
reference: &'a str,
|
||||
number: i32,
|
||||
}
|
||||
|
||||
#[derive(Hash2)]
|
||||
struct MultipleTypes<T, U> {
|
||||
first: T,
|
||||
second: U,
|
||||
}
|
||||
|
||||
#[derive(Hash2)]
|
||||
struct MultipleLifetimes<'a, 'b> {
|
||||
first: &'a str,
|
||||
second: &'b str,
|
||||
}
|
||||
|
||||
#[derive(Hash2)]
|
||||
struct Complex<'a, 'b, T, U>
|
||||
where
|
||||
T: Clone,
|
||||
U: Clone,
|
||||
{
|
||||
data1: T,
|
||||
data2: U,
|
||||
ref1: &'a str,
|
||||
ref2: &'b str,
|
||||
}
|
||||
|
||||
#[derive(Hash2)]
|
||||
enum SimpleEnum {
|
||||
A,
|
||||
B(u32),
|
||||
C { x: i32, y: i32 },
|
||||
}
|
||||
|
||||
#[derive(Hash2)]
|
||||
enum GenericEnum<T> {
|
||||
None,
|
||||
Some(T),
|
||||
Pair(T, T),
|
||||
}
|
||||
|
||||
fn hash_value<T: Hash2>(value: &T) -> u64 {
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
let mut hasher = DefaultHasher::new();
|
||||
value.hash(&mut hasher);
|
||||
std::hash::Hasher::finish(&hasher)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_struct() {
|
||||
let s = SimpleStruct { a: 42, b: 1000 };
|
||||
let hash1 = hash_value(&s);
|
||||
let hash2 = hash_value(&s);
|
||||
assert_eq!(hash1, hash2, "Same struct should produce same hash");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generic_struct() {
|
||||
let s1 = GenericStruct { value: 42u32, count: 10 };
|
||||
let s2 = GenericStruct { value: 42u32, count: 10 };
|
||||
assert_eq!(hash_value(&s1), hash_value(&s2));
|
||||
|
||||
let s3 = GenericStruct { value: "hello", count: 10 };
|
||||
let _ = hash_value(&s3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lifetime_struct() {
|
||||
let name = String::from("test");
|
||||
let s = LifetimeStruct { name: &name, id: 123 };
|
||||
let hash1 = hash_value(&s);
|
||||
let hash2 = hash_value(&s);
|
||||
assert_eq!(hash1, hash2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_both_generics() {
|
||||
let reference = String::from("ref");
|
||||
let s = BothGenerics {
|
||||
data: 42u32,
|
||||
reference: &reference,
|
||||
number: -10,
|
||||
};
|
||||
let _ = hash_value(&s);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_types() {
|
||||
let s = MultipleTypes {
|
||||
first: 42u32,
|
||||
second: "hello",
|
||||
};
|
||||
let _ = hash_value(&s);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_lifetimes() {
|
||||
let first = String::from("first");
|
||||
let second = String::from("second");
|
||||
let s = MultipleLifetimes {
|
||||
first: &first,
|
||||
second: &second,
|
||||
};
|
||||
let _ = hash_value(&s);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_complex() {
|
||||
let ref1 = String::from("ref1");
|
||||
let ref2 = String::from("ref2");
|
||||
let s = Complex {
|
||||
data1: 42u32,
|
||||
data2: "data",
|
||||
ref1: &ref1,
|
||||
ref2: &ref2,
|
||||
};
|
||||
let _ = hash_value(&s);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_enum() {
|
||||
let e1 = SimpleEnum::A;
|
||||
let e2 = SimpleEnum::B(42);
|
||||
let e3 = SimpleEnum::C { x: 1, y: 2 };
|
||||
|
||||
let _ = hash_value(&e1);
|
||||
let _ = hash_value(&e2);
|
||||
let _ = hash_value(&e3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generic_enum() {
|
||||
let e1: GenericEnum<u32> = GenericEnum::None;
|
||||
let e2 = GenericEnum::Some(42u32);
|
||||
let e3 = GenericEnum::Pair(1u32, 2u32);
|
||||
|
||||
let _ = hash_value(&e1);
|
||||
let _ = hash_value(&e2);
|
||||
let _ = hash_value(&e3);
|
||||
}
|
||||
Reference in New Issue
Block a user