1use core::fmt::Debug;
2
3use anyhash::Hasher;
4#[cfg(feature = "dev")]
5use arbitrary::Arbitrary;
6
7use compact_u64::{cu64_decode_canonic_standalone, cu64_decode_standalone};
8use ufotofu::codec_prelude::*;
9
10use crate::{groupings::Coordinatelike, prelude::*};
11
12#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, bon::Builder)]
49#[cfg_attr(feature = "dev", derive(Arbitrary))]
50#[builder(state_mod(vis = "pub"), on(_, overwritable))]
51pub struct Entry<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> {
52 pub(crate) namespace_id: N,
53 pub(crate) subspace_id: S,
54 pub(crate) path: Path<MCL, MCC, MPL>,
55 #[builder(into)]
56 pub(crate) timestamp: Timestamp,
57 pub(crate) payload_length: u64,
58 pub(crate) payload_digest: PD,
59}
60
61#[doc(hidden)]
62pub type BuilderComplete = entry_builder::SetPayloadLength<
63 entry_builder::SetPayloadDigest<
64 entry_builder::SetTimestamp<
65 entry_builder::SetPath<entry_builder::SetSubspaceId<entry_builder::SetNamespaceId>>,
66 >,
67 >,
68>;
69
70impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
71 Entry<MCL, MCC, MPL, N, S, PD>
72{
73 pub fn prefilled_builder<E>(
103 source: &E,
104 ) -> EntryBuilder<MCL, MCC, MPL, N, S, PD, BuilderComplete>
105 where
106 E: Entrylike<MCL, MCC, MPL, N, S, PD> + ?Sized,
107 N: Clone,
108 S: Clone,
109 PD: Clone,
110 {
111 Self::builder()
112 .namespace_id(source.wdm_namespace_id().clone())
113 .subspace_id(source.wdm_subspace_id().clone())
114 .path(source.wdm_path().clone())
115 .timestamp(source.wdm_timestamp())
116 .payload_digest(source.wdm_payload_digest().clone())
117 .payload_length(source.wdm_payload_length())
118 }
119
120 pub fn from_entrylike<E>(entrylike_to_clone: &E) -> Self
141 where
142 E: Entrylike<MCL, MCC, MPL, N, S, PD> + ?Sized,
143 N: Clone,
144 S: Clone,
145 PD: Clone,
146 {
147 Self::prefilled_builder(entrylike_to_clone).build()
148 }
149
150 pub fn into_authorised_entry<AT>(
171 self,
172 ingredients: &AT::Ingredients,
173 ) -> Result<AuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT>, AT::CreationError>
174 where
175 AT: AuthorisationToken<MCL, MCC, MPL, N, S, PD> + Debug,
176 N: Clone + Debug,
177 S: Clone + Debug,
178 PD: Clone + Debug,
179 {
180 let authorisation_token = AuthorisationToken::new_for_entry(&self, ingredients)?;
181
182 Ok(PossiblyAuthorisedEntry {
183 entry: self,
184 authorisation_token,
185 }
186 .into_authorised_entry().expect("`AuthorisationToken::new_for_entry` must produce an authorisation token that authorises the entry"))
187 }
188}
189
190impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Keylike<MCL, MCC, MPL, S>
191 for Entry<MCL, MCC, MPL, N, S, PD>
192{
193 fn wdm_subspace_id(&self) -> &S {
194 &self.subspace_id
195 }
196
197 fn wdm_path(&self) -> &Path<MCL, MCC, MPL> {
198 &self.path
199 }
200}
201
202impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
203 Coordinatelike<MCL, MCC, MPL, S> for Entry<MCL, MCC, MPL, N, S, PD>
204{
205 fn wdm_timestamp(&self) -> Timestamp {
206 self.timestamp
207 }
208}
209
210impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Namespaced<N>
211 for Entry<MCL, MCC, MPL, N, S, PD>
212{
213 fn wdm_namespace_id(&self) -> &N {
214 &self.namespace_id
215 }
216}
217
218impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
219 Entrylike<MCL, MCC, MPL, N, S, PD> for Entry<MCL, MCC, MPL, N, S, PD>
220{
221 fn wdm_payload_length(&self) -> u64 {
222 self.payload_length
223 }
224
225 fn wdm_payload_digest(&self) -> &PD {
226 &self.payload_digest
227 }
228}
229
230impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Encodable
232 for Entry<MCL, MCC, MPL, N, S, PD>
233where
234 N: Encodable,
235 S: Encodable,
236 PD: Encodable,
237{
238 async fn encode<C>(&self, consumer: &mut C) -> Result<(), C::Error>
239 where
240 C: BulkConsumer<Item = u8> + ?Sized,
241 {
242 self.wdm_encode_entry(consumer).await
243 }
244}
245
246impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> EncodableKnownLength
248 for Entry<MCL, MCC, MPL, N, S, PD>
249where
250 N: EncodableKnownLength,
251 S: EncodableKnownLength,
252 PD: EncodableKnownLength,
253{
254 fn len_of_encoding(&self) -> usize {
255 self.wdm_length_of_entry_encoding()
256 }
257}
258
259impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Decodable
261 for Entry<MCL, MCC, MPL, N, S, PD>
262where
263 N: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>>,
264 S: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>>,
265 PD: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>>,
266{
267 type ErrorReason = Blame;
268
269 async fn decode<P>(
270 producer: &mut P,
271 ) -> Result<Self, DecodeError<P::Final, P::Error, Self::ErrorReason>>
272 where
273 P: BulkProducer<Item = u8> + ?Sized,
274 Self: Sized,
275 {
276 Ok(Self {
277 namespace_id: producer
278 .produce_decoded_canonic()
279 .await
280 .map_err(|err| err.map_other(Into::into))?,
281 subspace_id: producer
282 .produce_decoded_canonic()
283 .await
284 .map_err(|err| err.map_other(Into::into))?,
285 path: producer.produce_decoded().await?,
286 timestamp: cu64_decode_standalone(producer)
287 .await
288 .map_err(|err| err.map_other(|_| unreachable!()))?
289 .into(),
290 payload_length: cu64_decode_standalone(producer)
291 .await
292 .map_err(|err| err.map_other(|_| unreachable!()))?,
293 payload_digest: producer
294 .produce_decoded_canonic()
295 .await
296 .map_err(|err| err.map_other(Into::into))?,
297 })
298 }
299}
300
301impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> DecodableCanonic
303 for Entry<MCL, MCC, MPL, N, S, PD>
304where
305 N: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>>,
306 S: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>>,
307 PD: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>>,
308{
309 type ErrorCanonic = Blame;
310
311 async fn decode_canonic<P>(
312 producer: &mut P,
313 ) -> Result<Self, DecodeError<P::Final, P::Error, Self::ErrorCanonic>>
314 where
315 P: BulkProducer<Item = u8> + ?Sized,
316 Self: Sized,
317 {
318 Ok(Self {
319 namespace_id: producer
320 .produce_decoded_canonic()
321 .await
322 .map_err(|err| err.map_other(Into::into))?,
323 subspace_id: producer
324 .produce_decoded_canonic()
325 .await
326 .map_err(|err| err.map_other(Into::into))?,
327 path: producer.produce_decoded_canonic().await?,
328 timestamp: cu64_decode_canonic_standalone(producer)
329 .await
330 .map_err(|err| err.map_other(|_| unreachable!()))?
331 .into(),
332 payload_length: cu64_decode_canonic_standalone(producer)
333 .await
334 .map_err(|err| err.map_other(|_| unreachable!()))?,
335 payload_digest: producer
336 .produce_decoded_canonic()
337 .await
338 .map_err(|err| err.map_other(Into::into))?,
339 })
340 }
341}
342
343impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, State: entry_builder::State>
344 EntryBuilder<MCL, MCC, MPL, N, S, PD, State>
345{
346 #[cfg(feature = "std")]
348 pub fn now(
349 self,
350 ) -> Result<
351 EntryBuilder<MCL, MCC, MPL, N, S, PD, entry_builder::SetTimestamp<State>>,
352 HifitimeError,
353 > {
354 Timestamp::now().map(|ts| self.timestamp(ts))
355 }
356
357 pub fn payload<Payload, H, Digest>(
361 self,
362 payload: Payload,
363 ) -> EntryBuilder<
364 MCL,
365 MCC,
366 MPL,
367 N,
368 S,
369 PD,
370 entry_builder::SetPayloadLength<entry_builder::SetPayloadDigest<State>>,
371 >
372 where
373 Payload: AsRef<[u8]>,
374 H: Default + Hasher<Digest>,
375 Digest: Into<PD>,
376 {
377 let mut hasher = H::default();
378 hasher.write(payload.as_ref());
379
380 let digest = hasher.finish().into();
381 let length = u64::try_from(payload.as_ref().len()).expect("payload too long");
383
384 self.payload_digest(digest).payload_length(length)
385 }
386
387 pub async fn payload_async<P, H, Digest>(
391 self,
392 payload_producer: &mut P,
393 ) -> Result<
394 EntryBuilder<
395 MCL,
396 MCC,
397 MPL,
398 N,
399 S,
400 PD,
401 entry_builder::SetPayloadLength<entry_builder::SetPayloadDigest<State>>,
402 >,
403 P::Error,
404 >
405 where
406 P: BulkProducer<Item = u8, Final = ()>,
407 H: Default + Hasher<Digest>,
408 Digest: Into<PD>,
409 {
410 let mut hasher = H::default();
411 let mut payload_len = 0;
412 loop {
413 match payload_producer
414 .expose_items_sync(|partial_payload| {
415 hasher.write(partial_payload);
416 payload_len += partial_payload.len();
417 (partial_payload.len(), ())
418 })
419 .await?
420 {
421 Either::Left(_) => {}
422 Either::Right(_) => {
423 let payload_digest = hasher.finish().into();
424 let payload_length = u64::try_from(payload_len).expect("payload too long");
426 return Ok(self
427 .payload_digest(payload_digest)
428 .payload_length(payload_length));
429 }
430 }
431 }
432 }
433}