1#[cfg(feature = "dev")]
2use arbitrary::{size_hint::and_all, Arbitrary};
3
4use crate::{
5 parameters::{AuthorisationToken, NamespaceId, PayloadDigest, SubspaceId},
6 path::Path,
7};
8
9pub type Timestamp = u64;
13
14#[derive(Debug, PartialEq, Eq, Clone)]
17pub struct Entry<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
18where
19 N: NamespaceId,
20 S: SubspaceId,
21 PD: PayloadDigest,
22{
23 namespace_id: N,
25 subspace_id: S,
27 path: Path<MCL, MCC, MPL>,
29 timestamp: Timestamp,
31 payload_length: u64,
33 payload_digest: PD,
35}
36
37impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Entry<MCL, MCC, MPL, N, S, PD>
38where
39 N: NamespaceId,
40 S: SubspaceId,
41 PD: PayloadDigest,
42{
43 pub fn new(
45 namespace_id: N,
46 subspace_id: S,
47 path: Path<MCL, MCC, MPL>,
48 timestamp: Timestamp,
49 payload_length: u64,
50 payload_digest: PD,
51 ) -> Self {
52 Entry {
53 namespace_id,
54 subspace_id,
55 path,
56 timestamp,
57 payload_length,
58 payload_digest,
59 }
60 }
61
62 pub fn namespace_id(&self) -> &N {
64 &self.namespace_id
65 }
66
67 pub fn subspace_id(&self) -> &S {
69 &self.subspace_id
70 }
71
72 pub fn path(&self) -> &Path<MCL, MCC, MPL> {
74 &self.path
75 }
76
77 pub fn timestamp(&self) -> Timestamp {
79 self.timestamp
80 }
81
82 pub fn payload_length(&self) -> u64 {
84 self.payload_length
85 }
86
87 pub fn payload_digest(&self) -> &PD {
89 &self.payload_digest
90 }
91
92 pub fn is_newer_than(&self, other: &Self) -> bool {
96 other.timestamp < self.timestamp
97 || (other.timestamp == self.timestamp && other.payload_digest < self.payload_digest)
98 || (other.timestamp == self.timestamp
99 && other.payload_digest == self.payload_digest
100 && other.payload_length < self.payload_length)
101 }
102}
103
104use syncify::syncify;
105use syncify::syncify_replace;
106
107#[syncify(encoding_sync)]
108mod encoding {
109 use super::*;
110
111 #[syncify_replace(use ufotofu::sync::{BulkConsumer, BulkProducer};)]
112 use ufotofu::local_nb::{BulkConsumer, BulkProducer};
113
114 use willow_encoding::{DecodeError, U64BE};
115
116 #[syncify_replace(use willow_encoding::sync::{Decodable, Encodable};)]
117 use willow_encoding::{Decodable, Encodable};
118
119 impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Encodable
120 for Entry<MCL, MCC, MPL, N, S, PD>
121 where
122 N: NamespaceId + Encodable,
123 S: SubspaceId + Encodable,
124 PD: PayloadDigest + Encodable,
125 {
126 async fn encode<C>(&self, consumer: &mut C) -> Result<(), <C>::Error>
127 where
128 C: BulkConsumer<Item = u8>,
129 {
130 self.namespace_id.encode(consumer).await?;
131 self.subspace_id.encode(consumer).await?;
132 self.path.encode(consumer).await?;
133
134 U64BE::from(self.timestamp).encode(consumer).await?;
135 U64BE::from(self.payload_length).encode(consumer).await?;
136
137 self.payload_digest.encode(consumer).await?;
138
139 Ok(())
140 }
141 }
142
143 impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Decodable
144 for Entry<MCL, MCC, MPL, N, S, PD>
145 where
146 N: NamespaceId + Decodable,
147 S: SubspaceId + Decodable,
148 PD: PayloadDigest + Decodable,
149 {
150 async fn decode<Prod>(producer: &mut Prod) -> Result<Self, DecodeError<Prod::Error>>
151 where
152 Prod: BulkProducer<Item = u8>,
153 {
154 let namespace_id = N::decode(producer).await?;
155 let subspace_id = S::decode(producer).await?;
156 let path = Path::<MCL, MCC, MPL>::decode(producer).await?;
157 let timestamp = U64BE::decode(producer).await?.into();
158 let payload_length = U64BE::decode(producer).await?.into();
159 let payload_digest = PD::decode(producer).await?;
160
161 Ok(Entry {
162 namespace_id,
163 subspace_id,
164 path,
165 timestamp,
166 payload_length,
167 payload_digest,
168 })
169 }
170 }
171}
172
173#[cfg(feature = "dev")]
174impl<'a, const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Arbitrary<'a>
175 for Entry<MCL, MCC, MPL, N, S, PD>
176where
177 N: NamespaceId + Arbitrary<'a>,
178 S: SubspaceId + Arbitrary<'a>,
179 PD: PayloadDigest + Arbitrary<'a>,
180{
181 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
182 let namespace_id: N = Arbitrary::arbitrary(u)?;
183
184 let subspace_id: S = Arbitrary::arbitrary(u)?;
185
186 let path: Path<MCL, MCC, MPL> = Arbitrary::arbitrary(u)?;
187
188 let payload_digest: PD = Arbitrary::arbitrary(u)?;
189
190 Ok(Self {
191 namespace_id,
192 subspace_id,
193 path,
194 payload_digest,
195 payload_length: Arbitrary::arbitrary(u)?,
196 timestamp: Arbitrary::arbitrary(u)?,
197 })
198 }
199
200 fn size_hint(depth: usize) -> (usize, Option<usize>) {
201 and_all(&[
202 N::size_hint(depth),
203 S::size_hint(depth),
204 Path::<MCL, MCC, MPL>::size_hint(depth),
205 PD::size_hint(depth),
206 u64::size_hint(depth),
207 u64::size_hint(depth),
208 ])
209 }
210}
211
212#[derive(Debug)]
214pub struct UnauthorisedWriteError;
215
216impl core::fmt::Display for UnauthorisedWriteError {
217 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218 write!(
219 f,
220 "Tried to authorise the writing of an entry using an AuthorisationToken which does not permit it."
221 )
222 }
223}
224
225impl std::error::Error for UnauthorisedWriteError {}
226
227pub struct AuthorisedEntry<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT>(
232 pub Entry<MCL, MCC, MPL, N, S, PD>,
233 pub AT,
234)
235where
236 N: NamespaceId,
237 S: SubspaceId,
238 PD: PayloadDigest;
239
240impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT>
241 AuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT>
242where
243 N: NamespaceId,
244 S: SubspaceId,
245 PD: PayloadDigest,
246{
247 pub fn new(
250 entry: Entry<MCL, MCC, MPL, N, S, PD>,
251 token: AT,
252 ) -> Result<Self, UnauthorisedWriteError>
253 where
254 AT: AuthorisationToken<MCL, MCC, MPL, N, S, PD>,
255 {
256 if token.is_authorised_write(&entry) {
257 return Ok(Self(entry, token));
258 }
259
260 Err(UnauthorisedWriteError)
261 }
262}
263
264#[cfg(test)]
265mod tests {
266 use crate::path::Component;
267
268 use super::*;
269
270 #[derive(Default, PartialEq, Eq, Clone)]
271 struct FakeNamespaceId(usize);
272 impl NamespaceId for FakeNamespaceId {}
273
274 #[derive(Default, PartialEq, Eq, PartialOrd, Ord, Clone)]
275 struct FakeSubspaceId(usize);
276 impl SubspaceId for FakeSubspaceId {
277 fn successor(&self) -> Option<Self> {
278 Some(FakeSubspaceId(self.0 + 1))
279 }
280 }
281
282 #[derive(Default, PartialEq, Eq, PartialOrd, Ord, Clone)]
283 struct FakePayloadDigest(usize);
284 impl PayloadDigest for FakePayloadDigest {}
285
286 const MCL: usize = 8;
287 const MCC: usize = 4;
288 const MPL: usize = 16;
289
290 #[test]
291 fn entry_newer_than() {
292 let e_a1 = Entry {
293 namespace_id: FakeNamespaceId::default(),
294 subspace_id: FakeSubspaceId::default(),
295 path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(b"a").unwrap()]).unwrap(),
296 payload_digest: FakePayloadDigest::default(),
297 payload_length: 0,
298 timestamp: 20,
299 };
300
301 let e_a2 = Entry {
302 namespace_id: FakeNamespaceId::default(),
303 subspace_id: FakeSubspaceId::default(),
304 path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(b"a").unwrap()]).unwrap(),
305 payload_digest: FakePayloadDigest::default(),
306 payload_length: 0,
307 timestamp: 10,
308 };
309
310 assert!(e_a1.is_newer_than(&e_a2));
311
312 let e_b1 = Entry {
313 namespace_id: FakeNamespaceId::default(),
314 subspace_id: FakeSubspaceId::default(),
315 path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(b"a").unwrap()]).unwrap(),
316 payload_digest: FakePayloadDigest(2),
317 payload_length: 0,
318 timestamp: 10,
319 };
320
321 let e_b2 = Entry {
322 namespace_id: FakeNamespaceId::default(),
323 subspace_id: FakeSubspaceId::default(),
324 path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(b"a").unwrap()]).unwrap(),
325 payload_digest: FakePayloadDigest(1),
326 payload_length: 0,
327 timestamp: 10,
328 };
329
330 assert!(e_b1.is_newer_than(&e_b2));
331
332 let e_c1 = Entry {
333 namespace_id: FakeNamespaceId::default(),
334 subspace_id: FakeSubspaceId::default(),
335 path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(b"a").unwrap()]).unwrap(),
336 payload_digest: FakePayloadDigest::default(),
337 payload_length: 2,
338 timestamp: 20,
339 };
340
341 let e_c2 = Entry {
342 namespace_id: FakeNamespaceId::default(),
343 subspace_id: FakeSubspaceId::default(),
344 path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(b"a").unwrap()]).unwrap(),
345 payload_digest: FakePayloadDigest::default(),
346 payload_length: 1,
347 timestamp: 20,
348 };
349
350 assert!(e_c1.is_newer_than(&e_c2));
351 }
352}