use std::collections::{HashMap, hash_map::DefaultHasher};
use std::fmt::Display;
use std::hash::{Hash, Hasher};
#[must_use]
pub fn make_hashable<T: Hash>(value: &T) -> u64 {
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
hasher.finish()
}
#[must_use]
pub fn make_hashable_slice<T: Hash>(items: &[T]) -> u64 {
let mut hasher = DefaultHasher::new();
for item in items {
item.hash(&mut hasher);
}
hasher.finish()
}
#[must_use]
pub fn make_hashable_map(map: &HashMap<String, String>) -> u64 {
let mut pairs = map.iter().collect::<Vec<_>>();
pairs.sort_unstable_by(|(left_key, _), (right_key, _)| left_key.cmp(right_key));
let mut hasher = DefaultHasher::new();
for (key, value) in pairs {
key.hash(&mut hasher);
value.hash(&mut hasher);
}
hasher.finish()
}
#[derive(Debug, Clone)]
pub struct HashableDisplay<T: Display>(pub T);
impl<T: Display> Hash for HashableDisplay<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.to_string().hash(state);
}
}
impl<T: Display> PartialEq for HashableDisplay<T> {
fn eq(&self, other: &Self) -> bool {
self.0.to_string() == other.0.to_string()
}
}
impl<T: Display> Eq for HashableDisplay<T> {}
#[cfg(test)]
mod tests {
use super::{HashableDisplay, make_hashable, make_hashable_map, make_hashable_slice};
use std::collections::HashMap;
#[test]
fn test_make_hashable_deterministic() {
let value = vec!["alpha", "beta", "gamma"];
assert_eq!(make_hashable(&value), make_hashable(&value));
}
#[test]
fn test_make_hashable_slice() {
let items = [1_u32, 2, 3, 4];
let reordered = [4_u32, 3, 2, 1];
assert_eq!(make_hashable_slice(&items), make_hashable_slice(&items));
assert_ne!(make_hashable_slice(&items), make_hashable_slice(&reordered));
}
#[test]
fn test_make_hashable_map() {
let mut first = HashMap::new();
first.insert("beta".to_string(), "2".to_string());
first.insert("alpha".to_string(), "1".to_string());
let mut second = HashMap::new();
second.insert("alpha".to_string(), "1".to_string());
second.insert("beta".to_string(), "2".to_string());
assert_eq!(make_hashable_map(&first), make_hashable_map(&second));
}
#[test]
fn test_hashable_display_eq() {
assert_eq!(HashableDisplay(42), HashableDisplay(42));
assert_ne!(HashableDisplay("42"), HashableDisplay("0042"));
}
}