1#[cfg(feature = "dev")]
2use arbitrary::Arbitrary;
3use compact_u64::CompactU64;
4
5use crate::{parameters::AuthorisationToken, path::Path};
6
7pub type Timestamp = u64;
12
13#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
17#[cfg_attr(feature = "dev", derive(Arbitrary))]
18pub struct Entry<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> {
19 namespace_id: N,
21 subspace_id: S,
23 path: Path<MCL, MCC, MPL>,
25 timestamp: Timestamp,
27 payload_digest: PD,
29 payload_length: u64,
31}
32
33impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
34 Entry<MCL, MCC, MPL, N, S, PD>
35{
36 pub fn new(
38 namespace_id: N,
39 subspace_id: S,
40 path: Path<MCL, MCC, MPL>,
41 timestamp: Timestamp,
42 payload_length: u64,
43 payload_digest: PD,
44 ) -> Self {
45 Entry {
46 namespace_id,
47 subspace_id,
48 path,
49 timestamp,
50 payload_length,
51 payload_digest,
52 }
53 }
54
55 pub fn namespace_id(&self) -> &N {
57 &self.namespace_id
58 }
59
60 pub fn subspace_id(&self) -> &S {
62 &self.subspace_id
63 }
64
65 pub fn path(&self) -> &Path<MCL, MCC, MPL> {
67 &self.path
68 }
69
70 pub fn timestamp(&self) -> Timestamp {
72 self.timestamp
73 }
74
75 pub fn payload_length(&self) -> u64 {
77 self.payload_length
78 }
79
80 pub fn payload_digest(&self) -> &PD {
82 &self.payload_digest
83 }
84}
85
86impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Entry<MCL, MCC, MPL, N, S, PD>
87where
88 PD: PartialOrd,
89{
90 pub fn is_newer_than(&self, other: &Self) -> bool {
95 other.timestamp < self.timestamp
96 || (other.timestamp == self.timestamp && other.payload_digest < self.payload_digest)
97 || (other.timestamp == self.timestamp
98 && other.payload_digest == self.payload_digest
99 && other.payload_length < self.payload_length)
100 }
101}
102
103use ufotofu::{BulkConsumer, BulkProducer};
104
105use ufotofu_codec::{
106 Blame, Decodable, DecodableCanonic, DecodableSync, DecodeError, Encodable, EncodableKnownSize,
107 EncodableSync,
108};
109
110impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Encodable
111 for Entry<MCL, MCC, MPL, N, S, PD>
112where
113 N: Encodable,
114 S: Encodable,
115 PD: Encodable,
116{
117 async fn encode<C>(&self, consumer: &mut C) -> Result<(), <C>::Error>
118 where
119 C: BulkConsumer<Item = u8>,
120 {
121 self.namespace_id.encode(consumer).await?;
122 self.subspace_id.encode(consumer).await?;
123 self.path.encode(consumer).await?;
124
125 CompactU64(self.timestamp).encode(consumer).await?;
126 CompactU64(self.payload_length).encode(consumer).await?;
127
128 self.payload_digest.encode(consumer).await?;
129
130 Ok(())
131 }
132}
133
134impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Decodable
135 for Entry<MCL, MCC, MPL, N, S, PD>
136where
137 N: Decodable,
138 S: Decodable,
139 PD: Decodable,
140 Blame: From<N::ErrorReason> + From<S::ErrorReason> + From<PD::ErrorReason>,
141{
142 type ErrorReason = Blame;
143
144 async fn decode<P>(
145 producer: &mut P,
146 ) -> Result<Self, ufotofu_codec::DecodeError<P::Final, P::Error, Self::ErrorReason>>
147 where
148 P: BulkProducer<Item = u8>,
149 Self: Sized,
150 {
151 let namespace_id = N::decode(producer)
152 .await
153 .map_err(DecodeError::map_other_from)?;
154 let subspace_id = S::decode(producer)
155 .await
156 .map_err(DecodeError::map_other_from)?;
157 let path = Path::<MCL, MCC, MPL>::decode(producer).await?;
158 let timestamp = CompactU64::decode(producer)
159 .await
160 .map_err(DecodeError::map_other_from)?
161 .0;
162 let payload_length = CompactU64::decode(producer)
163 .await
164 .map_err(DecodeError::map_other_from)?
165 .0;
166 let payload_digest = PD::decode(producer)
167 .await
168 .map_err(DecodeError::map_other_from)?;
169
170 Ok(Entry {
171 namespace_id,
172 subspace_id,
173 path,
174 timestamp,
175 payload_length,
176 payload_digest,
177 })
178 }
179}
180
181impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> DecodableCanonic
182 for Entry<MCL, MCC, MPL, N, S, PD>
183where
184 N: DecodableCanonic,
185 S: DecodableCanonic,
186 PD: DecodableCanonic,
187 Blame: From<N::ErrorReason>
188 + From<S::ErrorReason>
189 + From<PD::ErrorReason>
190 + From<N::ErrorCanonic>
191 + From<S::ErrorCanonic>
192 + From<PD::ErrorCanonic>,
193{
194 type ErrorCanonic = Blame;
195
196 async fn decode_canonic<P>(
197 producer: &mut P,
198 ) -> Result<Self, ufotofu_codec::DecodeError<P::Final, P::Error, Self::ErrorCanonic>>
199 where
200 P: BulkProducer<Item = u8>,
201 Self: Sized,
202 {
203 let namespace_id = N::decode_canonic(producer)
204 .await
205 .map_err(DecodeError::map_other_from)?;
206 let subspace_id = S::decode_canonic(producer)
207 .await
208 .map_err(DecodeError::map_other_from)?;
209 let path = Path::<MCL, MCC, MPL>::decode_canonic(producer).await?;
210 let timestamp = CompactU64::decode_canonic(producer)
211 .await
212 .map_err(DecodeError::map_other_from)?
213 .0;
214 let payload_length = CompactU64::decode_canonic(producer)
215 .await
216 .map_err(DecodeError::map_other_from)?
217 .0;
218 let payload_digest = PD::decode_canonic(producer)
219 .await
220 .map_err(DecodeError::map_other_from)?;
221
222 Ok(Entry {
223 namespace_id,
224 subspace_id,
225 path,
226 timestamp,
227 payload_length,
228 payload_digest,
229 })
230 }
231}
232
233impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> EncodableKnownSize
234 for Entry<MCL, MCC, MPL, N, S, PD>
235where
236 N: EncodableKnownSize,
237 S: EncodableKnownSize,
238 PD: EncodableKnownSize,
239{
240 fn len_of_encoding(&self) -> usize {
241 self.namespace_id.len_of_encoding()
242 + self.subspace_id.len_of_encoding()
243 + self.path.len_of_encoding()
244 + CompactU64(self.timestamp).len_of_encoding()
245 + CompactU64(self.payload_length).len_of_encoding()
246 + self.payload_digest.len_of_encoding()
247 }
248}
249
250impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> EncodableSync
251 for Entry<MCL, MCC, MPL, N, S, PD>
252where
253 N: EncodableSync,
254 S: EncodableSync,
255 PD: EncodableSync,
256{
257}
258
259impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> DecodableSync
260 for Entry<MCL, MCC, MPL, N, S, PD>
261where
262 N: DecodableSync,
263 S: DecodableSync,
264 PD: DecodableSync,
265 Blame: From<N::ErrorReason> + From<S::ErrorReason> + From<PD::ErrorReason>,
266{
267}
268
269#[derive(Debug)]
271pub struct UnauthorisedWriteError;
272
273impl core::fmt::Display for UnauthorisedWriteError {
274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275 write!(
276 f,
277 "Tried to authorise the writing of an entry using an AuthorisationToken which does not permit it."
278 )
279 }
280}
281
282impl std::error::Error for UnauthorisedWriteError {}
283
284#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
288pub struct AuthorisedEntry<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT>(
289 Entry<MCL, MCC, MPL, N, S, PD>,
290 AT,
291);
292
293impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT>
294 AuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT>
295where
296 AT: AuthorisationToken<MCL, MCC, MPL, N, S, PD>,
297{
298 pub fn new(
300 entry: Entry<MCL, MCC, MPL, N, S, PD>,
301 token: AT,
302 ) -> Result<Self, UnauthorisedWriteError>
303 where
304 AT: AuthorisationToken<MCL, MCC, MPL, N, S, PD>,
305 {
306 if token.is_authorised_write(&entry) {
307 return Ok(Self(entry, token));
308 }
309
310 Err(UnauthorisedWriteError)
311 }
312}
313
314impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT>
315 AuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT>
316{
317 pub unsafe fn new_unchecked(entry: Entry<MCL, MCC, MPL, N, S, PD>, token: AT) -> Self {
322 Self(entry, token)
323 }
324
325 pub fn into_parts(self) -> (Entry<MCL, MCC, MPL, N, S, PD>, AT) {
327 (self.0, self.1)
328 }
329
330 pub fn entry(&self) -> &Entry<MCL, MCC, MPL, N, S, PD> {
332 &self.0
333 }
334
335 pub fn token(&self) -> &AT {
337 &self.1
338 }
339}
340
341#[cfg(feature = "dev")]
342impl<'a, const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT> Arbitrary<'a>
343 for AuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT>
344where
345 N: Arbitrary<'a>,
346 S: Arbitrary<'a>,
347 PD: Arbitrary<'a>,
348 AT: AuthorisationToken<MCL, MCC, MPL, N, S, PD> + Arbitrary<'a>,
349{
350 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
351 let entry: Entry<MCL, MCC, MPL, N, S, PD> = Arbitrary::arbitrary(u)?;
352 let token: AT = Arbitrary::arbitrary(u)?;
353
354 if !token.is_authorised_write(&entry) {
355 arbitrary::Result::Err(arbitrary::Error::IncorrectFormat)
356 } else {
357 Ok(unsafe { Self::new_unchecked(entry, token) })
358 }
359 }
360}
361
362#[cfg(test)]
363mod tests {
364 use crate::path::Component;
365
366 use super::*;
367
368 const MCL: usize = 8;
369 const MCC: usize = 4;
370 const MPL: usize = 16;
371
372 #[test]
373 fn entry_newer_than() {
374 let e_a1 = Entry {
375 namespace_id: 0,
376 subspace_id: 0,
377 path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(b"a").unwrap()]).unwrap(),
378 payload_digest: 0,
379 payload_length: 0,
380 timestamp: 20,
381 };
382
383 let e_a2 = Entry {
384 namespace_id: 0,
385 subspace_id: 0,
386 path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(b"a").unwrap()]).unwrap(),
387 payload_digest: 0,
388 payload_length: 0,
389 timestamp: 10,
390 };
391
392 assert!(e_a1.is_newer_than(&e_a2));
393
394 let e_b1 = Entry {
395 namespace_id: 0,
396 subspace_id: 0,
397 path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(b"a").unwrap()]).unwrap(),
398 payload_digest: 2,
399 payload_length: 0,
400 timestamp: 10,
401 };
402
403 let e_b2 = Entry {
404 namespace_id: 0,
405 subspace_id: 0,
406 path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(b"a").unwrap()]).unwrap(),
407 payload_digest: 1,
408 payload_length: 0,
409 timestamp: 10,
410 };
411
412 assert!(e_b1.is_newer_than(&e_b2));
413
414 let e_c1 = Entry {
415 namespace_id: 0,
416 subspace_id: 0,
417 path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(b"a").unwrap()]).unwrap(),
418 payload_digest: 0,
419 payload_length: 2,
420 timestamp: 20,
421 };
422
423 let e_c2 = Entry {
424 namespace_id: 0,
425 subspace_id: 0,
426 path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(b"a").unwrap()]).unwrap(),
427 payload_digest: 0,
428 payload_length: 1,
429 timestamp: 20,
430 };
431
432 assert!(e_c1.is_newer_than(&e_c2));
433 }
434}