use std::{convert::TryInto, fmt}; use serde::{Deserialize, Serialize}; use sha2::{ digest::{generic_array::GenericArray, OutputSizeUser}, Digest as _, Sha256, }; use thiserror::Error; #[derive(Debug, Clone, PartialEq, Eq, Error)] pub enum DecodeError { #[error("invalid digest length {0}")] InvalidLength(usize), } /// A SHA-256 hash. #[derive(Serialize, Deserialize, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Digest([u8; 32]); impl Digest { pub fn new(bytes: impl AsRef<[u8]>) -> Self { Self::from(Sha256::digest(bytes)) } } impl AsRef<[u8; 32]> for Digest { fn as_ref(&self) -> &[u8; 32] { &self.0 } } impl fmt::Debug for Digest { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Hash({})", self) } } impl fmt::Display for Digest { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for byte in &self.0 { write!(f, "{:02x}", byte)?; } Ok(()) } } impl From<[u8; 32]> for Digest { fn from(bytes: [u8; 32]) -> Self { Self(bytes) } } impl TryFrom<&[u8]> for Digest { type Error = DecodeError; fn try_from(bytes: &[u8]) -> Result { let bytes: [u8; 32] = bytes .try_into() .map_err(|_| DecodeError::InvalidLength(bytes.len()))?; Ok(bytes.into()) } } impl From::OutputSize>> for Digest { fn from(array: GenericArray::OutputSize>) -> Self { Self(array.into()) } }