use std::{borrow::Cow, marker::PhantomData};
use litl::{impl_debug_as_litl, impl_single_tagged_data_serde, SingleTaggedData, TaggedDataError};
use serde::{de::DeserializeOwned, Serialize};
use serde_derive::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum StrongHash {
HashV1(blake3::Hash),
}
impl SingleTaggedData for StrongHash {
const TAG: &'static str = "hashV1";
fn as_bytes(&self) -> Cow<[u8]> {
match self {
StrongHash::HashV1(hash) => Cow::from(hash.as_bytes().as_ref()),
}
}
fn from_bytes(data: &[u8]) -> Result<Self, TaggedDataError>
where
Self: Sized,
{
let hash_bytes: [u8; 32] = data
.try_into()
.map_err(|err| TaggedDataError::data_error(Into::<StrongHashError>::into(err)))?;
Ok(StrongHash::HashV1(blake3::Hash::from(hash_bytes)))
}
}
#[derive(Debug, Error)]
pub enum StrongHashError {
#[error("Invalid StrongHash length")]
InvalidLength(#[from] std::array::TryFromSliceError),
}
impl_single_tagged_data_serde!(StrongHash);
impl_debug_as_litl!(StrongHash);
impl Ord for StrongHash {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_bytes().cmp(other.as_bytes())
}
}
impl PartialOrd for StrongHash {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl StrongHash {
pub fn as_bytes(&self) -> &[u8] {
match self {
StrongHash::HashV1(hash) => hash.as_bytes(),
}
}
}
#[derive(Clone, Default, Debug)]
pub struct StrongHasher(blake3::Hasher);
impl StrongHasher {
pub fn update(&mut self, data: &[u8]) -> &mut Self {
self.0.update(data);
self
}
pub fn finalize(&mut self) -> StrongHash {
StrongHash::HashV1(self.0.finalize())
}
}
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub struct HashOf<T> {
hash: StrongHash,
#[serde(default)]
_marker: PhantomData<T>,
}
impl<T: Serialize> HashOf<T> {
pub fn hash(value: &T) -> Self {
HashOf {
hash: StrongHasher::default()
.update(&litl::to_vec_canonical(value).unwrap())
.finalize(),
_marker: PhantomData,
}
}
pub fn as_value_hash(self) -> HashOf<litl::Val> {
HashOf {
hash: self.hash,
_marker: PhantomData,
}
}
}
impl HashOf<litl::Val> {
pub fn as_type_hash<D: DeserializeOwned>(self) -> HashOf<D> {
HashOf {
hash: self.hash,
_marker: PhantomData,
}
}
}
impl<T> PartialEq for HashOf<T> {
fn eq(&self, other: &Self) -> bool {
self.hash.eq(&other.hash)
}
}
impl<T> Eq for HashOf<T> {}
impl<T> Ord for HashOf<T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.hash.cmp(&other.hash)
}
}
impl<T> PartialOrd for HashOf<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<T> std::hash::Hash for HashOf<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.hash.hash(state)
}
}
impl<T> std::fmt::Debug for HashOf<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.hash.fmt(f)
}
}
impl<T> std::fmt::Display for HashOf<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.hash.fmt(f)
}
}
impl<T> std::str::FromStr for HashOf<T> {
type Err = <StrongHash as std::str::FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(HashOf {
hash: s.parse()?,
_marker: PhantomData,
})
}
}
impl<T> Clone for HashOf<T> {
fn clone(&self) -> Self {
Self {
hash: self.hash,
_marker: PhantomData,
}
}
}
impl<T> Copy for HashOf<T> {}