use {
core::{convert::Infallible, fmt, str::FromStr},
derive_more::Deref,
iroh::SecretKey,
serde::{Deserialize, Deserializer, Serialize, de},
};
pub type UniqueId = Digest;
pub type Tag = UniqueId;
#[derive(Clone, Copy, Deref)]
pub struct Digest(blake3::Hash);
impl From<&[u8]> for Digest {
fn from(s: &[u8]) -> Self {
match hex::decode(s) {
Ok(b) if b.len() == 32 => {
Self(blake3::Hash::from_slice(&b).expect("slice is 32 bytes"))
}
_ => Self(blake3::hash(s)),
}
}
}
impl From<&str> for Digest {
fn from(s: &str) -> Self {
Self::from(s.as_bytes())
}
}
impl PartialEq for Digest {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Eq for Digest {}
impl PartialOrd for Digest {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Digest {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.0.as_bytes().cmp(other.0.as_bytes())
}
}
impl core::hash::Hash for Digest {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.0.as_bytes().hash(state);
}
}
impl AsRef<[u8]> for Digest {
fn as_ref(&self) -> &[u8] {
self.0.as_bytes()
}
}
impl From<Digest> for [u8; 32] {
fn from(id: Digest) -> Self {
*id.0.as_bytes()
}
}
impl From<&Digest> for [u8; 32] {
fn from(id: &Digest) -> Self {
*id.0.as_bytes()
}
}
impl From<blake3::Hash> for Digest {
fn from(hash: blake3::Hash) -> Self {
Self(hash)
}
}
impl From<&blake3::Hash> for Digest {
fn from(hash: &blake3::Hash) -> Self {
Self(*hash)
}
}
impl From<Digest> for SecretKey {
fn from(val: Digest) -> Self {
Self::from_bytes(val.0.as_bytes())
}
}
impl PartialEq<&Self> for Digest {
fn eq(&self, other: &&Self) -> bool {
self.0 == other.0
}
}
impl PartialEq<Digest> for &Digest {
fn eq(&self, other: &Digest) -> bool {
self.0 == other.0
}
}
impl FromStr for Digest {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match hex::decode(s) {
Ok(b) if b.len() == 32 => Ok(Self(
blake3::Hash::from_slice(&b).expect("slice is 32 bytes"),
)),
_ => Ok(Self(blake3::hash(s.as_bytes()))),
}
}
}
impl fmt::Debug for Digest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0.to_hex())
}
}
impl fmt::Display for Digest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0.to_hex())
}
}
impl Serialize for Digest {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
serializer.serialize_str(self.0.to_hex().as_str())
} else {
self.0.serialize(serializer)
}
}
}
impl<'de> Deserialize<'de> for Digest {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if deserializer.is_human_readable() {
let s = String::deserialize(deserializer)?;
Ok(Self(blake3::Hash::from_hex(&s).map_err(de::Error::custom)?))
} else {
let bytes = <[u8; 32]>::deserialize(deserializer)?;
Ok(Self(blake3::Hash::from_bytes(bytes)))
}
}
}
impl Digest {
pub const fn zero() -> Self {
Self(blake3::Hash::from_bytes([0u8; 32]))
}
pub const fn as_bytes(&self) -> &[u8; 32] {
self.0.as_bytes()
}
#[expect(clippy::len_without_is_empty)]
pub const fn len(&self) -> usize {
32
}
pub fn from_hasher(hasher: &blake3::Hasher) -> Self {
Self(hasher.finalize())
}
pub fn from_parts(parts: &[impl AsRef<[u8]>]) -> Self {
let mut hasher = blake3::Hasher::new();
for part in parts {
hasher.update(part.as_ref());
}
Self::from_hasher(&hasher)
}
pub const fn from_u8(n: u8) -> Self {
let mut bytes = [0u8; 32];
bytes[31] = n;
Self(blake3::Hash::from_bytes(bytes))
}
pub fn from_u16(n: u16) -> Self {
let mut bytes = [0u8; 32];
bytes[30..32].copy_from_slice(&n.to_le_bytes());
Self(blake3::Hash::from_bytes(bytes))
}
pub fn from_u32(n: u32) -> Self {
let mut bytes = [0u8; 32];
bytes[28..32].copy_from_slice(&n.to_le_bytes());
Self(blake3::Hash::from_bytes(bytes))
}
pub fn from_u64(n: u64) -> Self {
let mut bytes = [0u8; 32];
bytes[24..32].copy_from_slice(&n.to_le_bytes());
Self(blake3::Hash::from_bytes(bytes))
}
pub fn from_u128(n: u128) -> Self {
let mut bytes = [0u8; 32];
bytes[16..32].copy_from_slice(&n.to_le_bytes());
Self(blake3::Hash::from_bytes(bytes))
}
pub const fn from_i8(n: i8) -> Self {
let mut bytes = [0u8; 32];
bytes[31] = n.to_le_bytes()[0];
Self(blake3::Hash::from_bytes(bytes))
}
pub fn from_i16(n: i16) -> Self {
let mut bytes = [0u8; 32];
bytes[30..32].copy_from_slice(&n.to_le_bytes());
Self(blake3::Hash::from_bytes(bytes))
}
pub fn from_i32(n: i32) -> Self {
let mut bytes = [0u8; 32];
bytes[28..32].copy_from_slice(&n.to_le_bytes());
Self(blake3::Hash::from_bytes(bytes))
}
pub fn from_i64(n: i64) -> Self {
let mut bytes = [0u8; 32];
bytes[24..32].copy_from_slice(&n.to_le_bytes());
Self(blake3::Hash::from_bytes(bytes))
}
pub fn from_i128(n: i128) -> Self {
let mut bytes = [0u8; 32];
bytes[16..32].copy_from_slice(&n.to_le_bytes());
Self(blake3::Hash::from_bytes(bytes))
}
pub fn from_usize(n: usize) -> Self {
let mut bytes = [0u8; 32];
bytes[32 - std::mem::size_of::<usize>()..32]
.copy_from_slice(&n.to_le_bytes());
Self(blake3::Hash::from_bytes(bytes))
}
pub fn from_isize(n: isize) -> Self {
let mut bytes = [0u8; 32];
bytes[32 - std::mem::size_of::<isize>()..32]
.copy_from_slice(&n.to_le_bytes());
Self(blake3::Hash::from_bytes(bytes))
}
pub const fn from_bytes(bytes: [u8; 32]) -> Self {
Self(blake3::Hash::from_bytes(bytes))
}
pub fn hash(bytes: &[u8]) -> Self {
blake3::hash(bytes).into()
}
pub fn random() -> Self {
Self(blake3::Hash::from_bytes(rand::random()))
}
#[must_use]
pub fn derive(&self, info: impl AsRef<[u8]>) -> Self {
let mut hasher = blake3::Hasher::new();
hasher.update(self.as_bytes());
hasher.update(info.as_ref());
Self(hasher.finalize())
}
#[must_use]
pub fn hashed(&self) -> Self {
let mut hasher = blake3::Hasher::new();
hasher.update(self.as_bytes());
Self::from_hasher(&hasher)
}
}
#[macro_export]
macro_rules! unique_id {
($s:expr) => {
$crate::primitives::UniqueId::from_bytes($crate::__unique_id_impl!($s))
};
}
#[macro_export]
macro_rules! id {
($s:expr) => {
$crate::primitives::UniqueId::from_bytes($crate::__unique_id_impl!($s))
};
}