conundrum 0.1.0

Hard-to-misuse crypto primitives with purpose scoping.
Documentation
use std::{marker::PhantomData};

use litl::{Litl, impl_debug_as_litl};
use serde::{Serialize, Deserializer, Serializer};
use serde_derive::{Serialize, Deserialize};

#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename = "Conundrum/Hash:Blake3")]
pub struct RawHash(
    #[serde(deserialize_with="deserialize_blake3")]
    #[serde(serialize_with="serialize_blake3")]
    blake3::Hash
);

impl_debug_as_litl!(RawHash);

use crate::serde_bytes_array;

fn deserialize_blake3<'de, D>(de: D) -> Result<blake3::Hash, D::Error> where D: Deserializer<'de> {
    let bytes: [u8; 32] = serde_bytes_array::deserialize(de)?;
    Ok(blake3::Hash::from(bytes))
}

fn serialize_blake3<S>(hash: &blake3::Hash, se: S) -> Result<S::Ok, S::Error> where S: Serializer {
    serde_bytes::Bytes::new(hash.as_bytes()).serialize(se)
}

impl Ord for RawHash {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.as_bytes().cmp(other.as_bytes())
    }
}

impl PartialOrd for RawHash {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl RawHash {
    pub fn as_bytes(&self) -> &[u8] {
        self.0.as_bytes()
    }
}

#[derive(Clone, Default)]
pub struct RawHasher(blake3::Hasher);

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

    pub fn finalize(&mut self) -> RawHash {
        RawHash(self.0.finalize())
    }
}

#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub struct HashOf<T> {
    hash: RawHash,
    #[serde(default)]
    _marker: PhantomData<T>,
}

impl<T: Serialize> HashOf<T> {
    pub fn hash(value: &T) -> Self {
        HashOf {
            hash: RawHasher::default()
                .update(&Litl::write_from(value))
                .finalize(),
            _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> Clone for HashOf<T> {
    fn clone(&self) -> Self {
        Self {
            hash: self.hash,
            _marker: PhantomData,
        }
    }
}

impl<T> Copy for HashOf<T> {}