use super::BASE32; #[derive(Debug)] pub enum Error { ReadHash(String), InvalidBytes, } pub const BLAKE3_BYTES: usize = 32; #[derive(Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord)] pub enum Hash { Blake3([u8; BLAKE3_BYTES]), } pub enum Hasher { Blake3(blake3::Hasher), } impl Default for Hasher { fn default() -> Self { Hasher::Blake3(blake3::Hasher::new()) } } impl Hasher { pub fn update(&mut self, bytes: &[u8]) { match self { Hasher::Blake3(ref mut h) => { h.update(bytes); } } } pub fn finish(&self) -> Hash { match self { Hasher::Blake3(ref h) => { let result = h.finalize(); let mut hash = [0; BLAKE3_BYTES]; hash.clone_from_slice(result.as_bytes()); Hash::Blake3(hash) } } } } impl std::fmt::Debug for Hash { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { write!(fmt, "{}", self.to_base32()) } } #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] enum Algo { Blake3 = 1, } impl Hash { pub fn to_bytes(self) -> [u8; 1 + BLAKE3_BYTES] { match self { Hash::Blake3(ref s) => { let mut out = [0; 1 + BLAKE3_BYTES]; out[0] = Algo::Blake3 as u8; out[1..].clone_from_slice(s); out } } } pub fn from_bytes(s: &[u8]) -> Option { if s.len() >= 1 + BLAKE3_BYTES && s[0] == Algo::Blake3 as u8 { let mut out = [0; BLAKE3_BYTES]; out.clone_from_slice(&s[1..]); Some(Hash::Blake3(out)) } else { None } } pub fn validate(s: &[u8]) -> Result<(), Error> { if s.len() >= 1 + BLAKE3_BYTES && s[0] == Algo::Blake3 as u8 { Ok(()) } else { Err(Error::InvalidBytes) } } pub fn to_base32(self) -> String { let hash = self.to_bytes(); BASE32.encode(&hash) } pub fn from_base32(s: &[u8]) -> Option { let bytes = BASE32.decode(s).ok()?; Self::from_bytes(&bytes) } } impl std::str::FromStr for Hash { type Err = Error; fn from_str(s: &str) -> Result { if let Some(b) = Self::from_base32(s.as_bytes()) { Ok(b) } else { Err(Error::ReadHash(s.to_string())) } } }