use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Hash([u8; 32]);
impl Hash {
pub const ZERO: Hash = Hash([0u8; 32]);
pub fn from_bytes(bytes: [u8; 32]) -> Self {
Hash(bytes)
}
pub fn digest(data: &[u8]) -> Self {
let hash = blake3::hash(data);
Hash(*hash.as_bytes())
}
pub fn digest_many(parts: &[&[u8]]) -> Self {
let mut hasher = blake3::Hasher::new();
for part in parts {
hasher.update(part);
}
Hash(*hasher.finalize().as_bytes())
}
pub fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
pub fn to_hex(&self) -> String {
hex::encode(self.0)
}
pub fn from_hex(s: &str) -> Result<Self, hex::FromHexError> {
let bytes = hex::decode(s)?;
if bytes.len() != 32 {
return Err(hex::FromHexError::InvalidStringLength);
}
let mut arr = [0u8; 32];
arr.copy_from_slice(&bytes);
Ok(Hash(arr))
}
pub fn short(&self) -> String {
self.to_hex()[..7].to_string()
}
pub fn is_zero(&self) -> bool {
self.0 == [0u8; 32]
}
}
impl fmt::Display for Hash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_hex())
}
}
impl fmt::Debug for Hash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Hash({})", self.short())
}
}
impl Default for Hash {
fn default() -> Self {
Hash::ZERO
}
}
impl AsRef<[u8]> for Hash {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hash_digest() {
let h1 = Hash::digest(b"hello");
let h2 = Hash::digest(b"hello");
let h3 = Hash::digest(b"world");
assert_eq!(h1, h2);
assert_ne!(h1, h3);
}
#[test]
fn test_hash_hex_roundtrip() {
let h1 = Hash::digest(b"test data");
let hex = h1.to_hex();
let h2 = Hash::from_hex(&hex).unwrap();
assert_eq!(h1, h2);
}
#[test]
fn test_hash_short() {
let h = Hash::digest(b"test");
assert_eq!(h.short().len(), 7);
}
}