use crate::errors::ProllyTreeError;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct ValueDigest<const N: usize>(pub [u8; N]);
impl<const N: usize> ValueDigest<N> {
pub fn new(data: &[u8]) -> Self {
assert!(
N <= 32,
"N must be less than or equal to 32 due to SHA-256 output size"
);
let mut hasher = Sha256::new();
hasher.update(data);
let result = hasher.finalize();
let mut hash = [0u8; N];
hash.copy_from_slice(&result[..N]);
ValueDigest(hash)
}
pub fn raw_hash(data: &[u8]) -> Self {
ValueDigest(<[u8; N]>::try_from(data).expect("data length must match digest size N"))
}
pub fn try_raw_hash(data: &[u8]) -> Result<Self, ProllyTreeError> {
let arr = <[u8; N]>::try_from(data).map_err(|_| ProllyTreeError::InvalidDigestLength)?;
Ok(ValueDigest(arr))
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn combine(lhs: &Self, rhs: &Self) -> Self {
let mut combined_data = vec![];
combined_data.extend_from_slice(&lhs.0);
combined_data.extend_from_slice(&rhs.0);
Self::new(&combined_data)
}
}
impl<const N: usize> Default for ValueDigest<N> {
fn default() -> Self {
ValueDigest([0u8; N])
}
}
impl<const N: usize> AsRef<[u8]> for ValueDigest<N> {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl<const N: usize> Serialize for ValueDigest<N> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_bytes(&self.0)
}
}
impl<'de, const N: usize> Deserialize<'de> for ValueDigest<N> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let bytes: Vec<u8> = serde::de::Deserialize::deserialize(deserializer)?;
let array = <[u8; N]>::try_from(bytes.as_slice()).map_err(|_| {
serde::de::Error::invalid_length(bytes.len(), &format!("array of length {N}").as_str())
})?;
Ok(ValueDigest(array))
}
}
#[cfg(test)]
mod tests {
use super::*;
use sha2::{Digest, Sha256};
#[test]
fn test_value_digest_new() {
let data = b"test data";
let expected_hash = {
let mut hasher = Sha256::new();
hasher.update(data);
let result = hasher.finalize();
let mut hash = [0u8; 32];
hash.copy_from_slice(&result[..32]);
hash
};
let value_digest = ValueDigest::<32>::new(data);
assert_eq!(value_digest.as_bytes(), &expected_hash);
}
#[test]
fn test_value_digest_as_bytes() {
let data = b"test data";
let value_digest = ValueDigest::<32>::new(data);
let hash_bytes = value_digest.as_bytes();
assert_eq!(hash_bytes.len(), 32);
}
#[test]
fn test_value_digest_equality() {
let data1 = b"test data 1";
let data2 = b"test data 2";
let digest1 = ValueDigest::<32>::new(data1);
let digest2 = ValueDigest::<32>::new(data1);
let digest3 = ValueDigest::<32>::new(data2);
assert_eq!(digest1, digest2);
assert_ne!(digest1, digest3);
}
#[test]
fn test_value_digest_clone() {
let data = b"test data";
let value_digest = ValueDigest::<32>::new(data);
let value_digest_clone = value_digest.clone();
assert_eq!(value_digest, value_digest_clone);
}
#[test]
fn test_value_digest_raw_hash() {
let data = b"test data";
let value_digest1 = ValueDigest::<32>::new(data);
let value_digest2 = ValueDigest::<32>::raw_hash(value_digest1.as_bytes());
assert_eq!(value_digest1, value_digest2);
}
}