s2_common/
bash.rs

1use bytes::Bytes;
2
3/// BLAKE3 hash (32 bytes) of any number of fields.
4///
5/// Default SerDe implementation uses hex representation.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7pub struct Bash(blake3::Hash);
8
9impl Bash {
10    pub const LEN: usize = 32;
11    const SEPARATOR: u8 = 0u8;
12
13    pub fn new(components: &[&[u8]]) -> Self {
14        let mut hasher = blake3::Hasher::new();
15        for component in components {
16            hasher.update(component);
17            hasher.update(&[Self::SEPARATOR]);
18        }
19        Self(hasher.finalize())
20    }
21
22    pub fn as_bytes(&self) -> &[u8; Bash::LEN] {
23        self.0.as_bytes()
24    }
25}
26
27impl std::fmt::Display for Bash {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        f.write_str(self.0.to_hex().as_str())
30    }
31}
32
33impl AsRef<[u8]> for Bash {
34    fn as_ref(&self) -> &[u8] {
35        self.as_bytes()
36    }
37}
38
39impl From<Bash> for [u8; Bash::LEN] {
40    fn from(bash: Bash) -> Self {
41        bash.0.into()
42    }
43}
44
45impl From<[u8; Bash::LEN]> for Bash {
46    fn from(bytes: [u8; Bash::LEN]) -> Self {
47        Self(blake3::Hash::from_bytes(bytes))
48    }
49}
50
51impl From<Bash> for Bytes {
52    fn from(bash: Bash) -> Self {
53        Bytes::copy_from_slice(bash.as_bytes())
54    }
55}
56
57impl serde::Serialize for Bash {
58    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
59    where
60        S: serde::Serializer,
61    {
62        serializer.serialize_str(&self.0.to_hex())
63    }
64}
65
66impl<'de> serde::Deserialize<'de> for Bash {
67    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
68    where
69        D: serde::Deserializer<'de>,
70    {
71        let s = String::deserialize(deserializer)?;
72        let hash = blake3::Hash::from_hex(s.as_bytes()).map_err(serde::de::Error::custom)?;
73        Ok(Self(hash))
74    }
75}