zoe_wire_protocol/
primitives.rs

1use crate::Hash;
2use serde::{Deserialize, Serialize};
3use std::ops::Deref;
4
5#[cfg(feature = "frb-api")]
6use flutter_rust_bridge::frb;
7
8/// A unique identifier for cryptographic keys
9///
10/// This is a Blake3 hash that uniquely identifies a key, typically computed
11/// from the key's public bytes or other identifying information.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13pub struct KeyId(pub Hash);
14
15impl KeyId {
16    /// Create a new KeyId from a Hash
17    pub fn new(hash: Hash) -> Self {
18        Self(hash)
19    }
20
21    /// Create a KeyId from raw bytes
22    pub fn from_bytes(bytes: [u8; 32]) -> Self {
23        Self(Hash::from(bytes))
24    }
25
26    /// Get the underlying bytes
27    pub fn as_bytes(&self) -> &[u8; 32] {
28        self.0.as_bytes()
29    }
30
31    /// Convert to hex string for display
32    pub fn to_hex(&self) -> String {
33        hex::encode(self.as_bytes())
34    }
35}
36
37impl Deref for KeyId {
38    type Target = Hash;
39
40    fn deref(&self) -> &Self::Target {
41        &self.0
42    }
43}
44
45impl From<Hash> for KeyId {
46    fn from(hash: Hash) -> Self {
47        Self(hash)
48    }
49}
50
51impl From<[u8; 32]> for KeyId {
52    fn from(bytes: [u8; 32]) -> Self {
53        Self(Hash::from(bytes))
54    }
55}
56
57impl std::fmt::Display for KeyId {
58    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59        write!(f, "{}", self.to_hex())
60    }
61}
62
63impl AsRef<[u8]> for KeyId {
64    fn as_ref(&self) -> &[u8] {
65        self.0.as_bytes()
66    }
67}
68
69impl PartialOrd for KeyId {
70    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
71        Some(self.cmp(other))
72    }
73}
74
75impl Ord for KeyId {
76    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
77        self.0.as_bytes().cmp(other.0.as_bytes())
78    }
79}
80
81#[cfg(feature = "rusqlite")]
82impl rusqlite::ToSql for KeyId {
83    fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
84        Ok(rusqlite::types::ToSqlOutput::from(
85            self.as_bytes().as_slice(),
86        ))
87    }
88}
89
90#[cfg(feature = "rusqlite")]
91impl rusqlite::types::FromSql for KeyId {
92    fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
93        match value {
94            rusqlite::types::ValueRef::Blob(bytes) => {
95                if bytes.len() != 32 {
96                    return Err(rusqlite::types::FromSqlError::InvalidBlobSize {
97                        expected_size: 32,
98                        blob_size: bytes.len(),
99                    });
100                }
101                let mut array = [0u8; 32];
102                array.copy_from_slice(bytes);
103                Ok(KeyId::from_bytes(array))
104            }
105            _ => Err(rusqlite::types::FromSqlError::InvalidType),
106        }
107    }
108}
109
110/// A unique identifier for blobs
111///
112/// This is a Blake3 hash that uniquely identifies blob content, computed
113/// from the blob's raw bytes.
114#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
115pub struct BlobId(pub Hash);
116
117impl BlobId {
118    /// Create a new BlobId from a Hash
119    pub fn new(hash: Hash) -> Self {
120        Self(hash)
121    }
122
123    /// Create a BlobId by hashing the given content
124    pub fn from_content(content: &[u8]) -> Self {
125        Self(crate::hash(content))
126    }
127
128    /// Create a BlobId from raw bytes
129    pub fn from_bytes(bytes: [u8; 32]) -> Self {
130        Self(Hash::from(bytes))
131    }
132
133    /// Get the underlying bytes
134    pub fn as_bytes(&self) -> &[u8; 32] {
135        self.0.as_bytes()
136    }
137
138    /// Convert to hex string for display
139    pub fn to_hex(&self) -> String {
140        hex::encode(self.as_bytes())
141    }
142}
143
144impl Deref for BlobId {
145    type Target = Hash;
146
147    fn deref(&self) -> &Self::Target {
148        &self.0
149    }
150}
151
152impl From<Hash> for BlobId {
153    fn from(hash: Hash) -> Self {
154        Self(hash)
155    }
156}
157
158impl From<[u8; 32]> for BlobId {
159    fn from(bytes: [u8; 32]) -> Self {
160        Self(Hash::from(bytes))
161    }
162}
163
164impl std::fmt::Display for BlobId {
165    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166        write!(f, "{}", self.to_hex())
167    }
168}
169
170impl AsRef<[u8]> for BlobId {
171    fn as_ref(&self) -> &[u8] {
172        self.0.as_bytes()
173    }
174}
175
176impl PartialOrd for BlobId {
177    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
178        Some(self.cmp(other))
179    }
180}
181
182impl Ord for BlobId {
183    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
184        self.0.as_bytes().cmp(other.0.as_bytes())
185    }
186}
187
188#[cfg(feature = "rusqlite")]
189impl rusqlite::ToSql for BlobId {
190    fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
191        Ok(rusqlite::types::ToSqlOutput::from(
192            self.as_bytes().as_slice(),
193        ))
194    }
195}
196
197#[cfg(feature = "rusqlite")]
198impl rusqlite::types::FromSql for BlobId {
199    fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
200        match value {
201            rusqlite::types::ValueRef::Blob(bytes) => {
202                if bytes.len() != 32 {
203                    return Err(rusqlite::types::FromSqlError::InvalidBlobSize {
204                        expected_size: 32,
205                        blob_size: bytes.len(),
206                    });
207                }
208                let mut array = [0u8; 32];
209                array.copy_from_slice(bytes);
210                Ok(BlobId::from_bytes(array))
211            }
212            _ => Err(rusqlite::types::FromSqlError::InvalidType),
213        }
214    }
215}
216
217/// A unique identifier for messages
218///
219/// This is a Blake3 hash that uniquely identifies message content, computed
220/// from the message's serialized bytes (excluding signature to handle ML-DSA randomness).
221#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
222#[cfg_attr(feature = "frb-api", frb(opaque))]
223pub struct MessageId(pub Hash);
224
225#[cfg_attr(feature = "frb-api", frb)]
226impl MessageId {
227    /// Create a new MessageId from a Hash
228    pub fn new(hash: Hash) -> Self {
229        Self(hash)
230    }
231
232    /// Create a MessageId by hashing the given content
233    pub fn from_content(content: &[u8]) -> Self {
234        Self(crate::hash(content))
235    }
236
237    /// Create a MessageId from raw bytes
238    pub fn from_bytes(bytes: [u8; 32]) -> Self {
239        Self(Hash::from(bytes))
240    }
241
242    /// Get the underlying bytes
243    pub fn as_bytes(&self) -> &[u8; 32] {
244        self.0.as_bytes()
245    }
246
247    /// Convert to hex string for display
248    pub fn to_hex(&self) -> String {
249        hex::encode(self.as_bytes())
250    }
251}
252
253impl Deref for MessageId {
254    type Target = Hash;
255
256    fn deref(&self) -> &Self::Target {
257        &self.0
258    }
259}
260
261impl From<Hash> for MessageId {
262    fn from(hash: Hash) -> Self {
263        Self(hash)
264    }
265}
266
267impl From<[u8; 32]> for MessageId {
268    fn from(bytes: [u8; 32]) -> Self {
269        Self(Hash::from(bytes))
270    }
271}
272
273impl std::fmt::Display for MessageId {
274    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275        write!(f, "{}", self.to_hex())
276    }
277}
278
279impl AsRef<[u8]> for MessageId {
280    fn as_ref(&self) -> &[u8] {
281        self.0.as_bytes()
282    }
283}
284
285impl PartialOrd for MessageId {
286    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
287        Some(self.cmp(other))
288    }
289}
290
291impl Ord for MessageId {
292    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
293        self.0.as_bytes().cmp(other.0.as_bytes())
294    }
295}
296
297#[cfg(feature = "rusqlite")]
298impl rusqlite::ToSql for MessageId {
299    fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
300        Ok(rusqlite::types::ToSqlOutput::from(
301            self.as_bytes().as_slice(),
302        ))
303    }
304}
305
306#[cfg(feature = "rusqlite")]
307impl rusqlite::types::FromSql for MessageId {
308    fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
309        match value {
310            rusqlite::types::ValueRef::Blob(bytes) => {
311                if bytes.len() != 32 {
312                    return Err(rusqlite::types::FromSqlError::InvalidBlobSize {
313                        expected_size: 32,
314                        blob_size: bytes.len(),
315                    });
316                }
317                let mut array = [0u8; 32];
318                array.copy_from_slice(bytes);
319                Ok(MessageId::from_bytes(array))
320            }
321            _ => Err(rusqlite::types::FromSqlError::InvalidType),
322        }
323    }
324}
325
326/// Legacy type alias for backward compatibility
327#[deprecated(note = "Use KeyId instead")]
328pub type Id = [u8; 32];