use sha2::{Digest, Sha256};
pub(crate) fn hash<T: serde::Serialize>(value: &T) -> String {
let bytes = serde_json::to_vec(value).expect("canonical serialization");
let digest = Sha256::digest(&bytes);
let mut out = String::with_capacity(digest.len() * 2);
for b in digest.iter() {
out.push_str(&format!("{b:02x}"));
}
out
}
#[cfg(test)]
mod tests {
use super::hash;
use serde::Serialize;
#[derive(Serialize)]
struct Point { x: i32, y: i32 }
#[test]
fn identical_values_hash_equal() {
let a = Point { x: 1, y: 2 };
let b = Point { x: 1, y: 2 };
assert_eq!(hash(&a), hash(&b));
}
#[test]
fn different_values_hash_differently() {
let a = Point { x: 1, y: 2 };
let b = Point { x: 2, y: 1 };
assert_ne!(hash(&a), hash(&b));
}
#[test]
fn hash_is_64_char_lowercase_hex() {
let h = hash(&Point { x: 0, y: 0 });
assert_eq!(h.len(), 64);
assert!(h.chars().all(|c| c.is_ascii_hexdigit() && (c.is_ascii_digit() || c.is_lowercase())));
}
}