fermah-common 0.1.2

Fermah common utilities and types.
Documentation
use std::fmt::{Debug, Display};

use const_hex::{traits::FromHex, FromHexError, ToHexExt};
use ethers::abi::FixedBytes;
use serde::{Deserialize, Serialize};

use crate::hash::Hasher;

#[derive(Serialize, Deserialize, Clone, Copy, Hash, Eq, PartialEq)]
pub struct Blake3Hash(pub blake3::Hash);

impl Blake3Hash {
    pub fn as_32_bytes(&self) -> &[u8; 32] {
        self.0.as_bytes()
    }
}

impl AsRef<[u8]> for Blake3Hash {
    fn as_ref(&self) -> &[u8] {
        self.0.as_bytes()
    }
}

impl From<[u8; 32]> for Blake3Hash {
    fn from(value: [u8; 32]) -> Self {
        let h: blake3::Hash = value.into();
        Blake3Hash(h)
    }
}

impl From<FixedBytes> for Blake3Hash {
    fn from(value: FixedBytes) -> Self {
        assert_eq!(value.len(), 32, "Length of the array is not equal to 32");
        let mut buf = [0_u8; 32];

        for (i, b) in value.into_iter().enumerate() {
            buf[i] = b;
        }
        buf.into()
    }
}

impl Debug for Blake3Hash {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.encode_hex_with_prefix())
    }
}

impl Display for Blake3Hash {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.encode_hex_with_prefix())
    }
}

impl FromHex for Blake3Hash {
    type Error = FromHexError;

    fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
        let bytes = <Vec<u8>>::from_hex(hex)?;
        let fixed: [u8; 32] = bytes
            .as_slice()
            .try_into()
            .map_err(|_| FromHexError::InvalidStringLength)?;
        Ok(Blake3Hash(fixed.into()))
    }
}

#[derive(Clone)]
pub struct Blake3Hasher(pub blake3::Hasher);

impl Hasher for Blake3Hasher {
    type Hash = Blake3Hash;

    fn new() -> Self {
        Self(blake3::Hasher::new())
    }

    fn update(&mut self, data: &[u8]) -> &mut Self {
        self.0.update(data);
        self
    }

    fn update_mmap_rayon(&mut self, path: &std::path::Path) -> Result<(), std::io::Error> {
        self.0.update_mmap_rayon(path)?;
        Ok(())
    }

    fn update_mmap(&mut self, path: &std::path::Path) -> Result<(), std::io::Error> {
        self.0.update_mmap(path)?;
        Ok(())
    }

    fn finalize(self) -> Self::Hash {
        Blake3Hash(self.0.finalize())
    }
}

#[cfg(test)]
mod tests {
    use crate::hash::{blake3::Blake3Hasher, Hashable};

    struct TestHashable {
        data: String,
    }

    impl Hashable for TestHashable {
        fn collect(&self) -> std::borrow::Cow<[u8]> {
            self.data.as_bytes().into()
        }
    }

    #[test]
    fn test_hasher() {
        let hex = "4878ca0425c739fa427f7eda20fe845f6b2e46ba5fe2a14df5b1e32f50603215";

        let th = TestHashable {
            data: "test".to_string(),
        };

        let hash = th.hash::<Blake3Hasher>().0;
        assert_eq!(hash.to_hex().as_str(), hex);

        let hexed = blake3::Hash::from_hex(hex).unwrap();
        assert_eq!(hash, hexed);
    }
}