use core::fmt::Debug;
use anyhash::Hasher;
#[cfg(feature = "dev")]
use arbitrary::Arbitrary;
use compact_u64::{cu64_decode_canonic_standalone, cu64_decode_standalone};
use ufotofu::codec_prelude::*;
use crate::{groupings::Coordinatelike, prelude::*};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, bon::Builder)]
#[cfg_attr(feature = "dev", derive(Arbitrary))]
#[builder(state_mod(vis = "pub"), on(_, overwritable))]
pub struct Entry<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> {
pub(crate) namespace_id: N,
pub(crate) subspace_id: S,
pub(crate) path: Path<MCL, MCC, MPL>,
#[builder(into)]
pub(crate) timestamp: Timestamp,
pub(crate) payload_length: u64,
pub(crate) payload_digest: PD,
}
#[doc(hidden)]
pub type BuilderComplete = entry_builder::SetPayloadLength<
entry_builder::SetPayloadDigest<
entry_builder::SetTimestamp<
entry_builder::SetPath<entry_builder::SetSubspaceId<entry_builder::SetNamespaceId>>,
>,
>,
>;
impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
Entry<MCL, MCC, MPL, N, S, PD>
{
pub fn prefilled_builder<E>(
source: &E,
) -> EntryBuilder<MCL, MCC, MPL, N, S, PD, BuilderComplete>
where
E: Entrylike<MCL, MCC, MPL, N, S, PD> + ?Sized,
N: Clone,
S: Clone,
PD: Clone,
{
Self::builder()
.namespace_id(source.wdm_namespace_id().clone())
.subspace_id(source.wdm_subspace_id().clone())
.path(source.wdm_path().clone())
.timestamp(source.wdm_timestamp())
.payload_digest(source.wdm_payload_digest().clone())
.payload_length(source.wdm_payload_length())
}
pub fn from_entrylike<E>(entrylike_to_clone: &E) -> Self
where
E: Entrylike<MCL, MCC, MPL, N, S, PD> + ?Sized,
N: Clone,
S: Clone,
PD: Clone,
{
Self::prefilled_builder(entrylike_to_clone).build()
}
pub fn into_authorised_entry<AT>(
self,
ingredients: &AT::Ingredients,
) -> Result<AuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT>, AT::CreationError>
where
AT: AuthorisationToken<MCL, MCC, MPL, N, S, PD> + Debug,
N: Clone + Debug,
S: Clone + Debug,
PD: Clone + Debug,
{
let authorisation_token = AuthorisationToken::new_for_entry(&self, ingredients)?;
Ok(PossiblyAuthorisedEntry {
entry: self,
authorisation_token,
}
.into_authorised_entry().expect("`AuthorisationToken::new_for_entry` must produce an authorisation token that authorises the entry"))
}
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Keylike<MCL, MCC, MPL, S>
for Entry<MCL, MCC, MPL, N, S, PD>
{
fn wdm_subspace_id(&self) -> &S {
&self.subspace_id
}
fn wdm_path(&self) -> &Path<MCL, MCC, MPL> {
&self.path
}
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
Coordinatelike<MCL, MCC, MPL, S> for Entry<MCL, MCC, MPL, N, S, PD>
{
fn wdm_timestamp(&self) -> Timestamp {
self.timestamp
}
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Namespaced<N>
for Entry<MCL, MCC, MPL, N, S, PD>
{
fn wdm_namespace_id(&self) -> &N {
&self.namespace_id
}
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
Entrylike<MCL, MCC, MPL, N, S, PD> for Entry<MCL, MCC, MPL, N, S, PD>
{
fn wdm_payload_length(&self) -> u64 {
self.payload_length
}
fn wdm_payload_digest(&self) -> &PD {
&self.payload_digest
}
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Encodable
for Entry<MCL, MCC, MPL, N, S, PD>
where
N: Encodable,
S: Encodable,
PD: Encodable,
{
async fn encode<C>(&self, consumer: &mut C) -> Result<(), C::Error>
where
C: BulkConsumer<Item = u8> + ?Sized,
{
self.wdm_encode_entry(consumer).await
}
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> EncodableKnownLength
for Entry<MCL, MCC, MPL, N, S, PD>
where
N: EncodableKnownLength,
S: EncodableKnownLength,
PD: EncodableKnownLength,
{
fn len_of_encoding(&self) -> usize {
self.wdm_length_of_entry_encoding()
}
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Decodable
for Entry<MCL, MCC, MPL, N, S, PD>
where
N: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>>,
S: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>>,
PD: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>>,
{
type ErrorReason = Blame;
async fn decode<P>(
producer: &mut P,
) -> Result<Self, DecodeError<P::Final, P::Error, Self::ErrorReason>>
where
P: BulkProducer<Item = u8> + ?Sized,
Self: Sized,
{
Ok(Self {
namespace_id: producer
.produce_decoded_canonic()
.await
.map_err(|err| err.map_other(Into::into))?,
subspace_id: producer
.produce_decoded_canonic()
.await
.map_err(|err| err.map_other(Into::into))?,
path: producer.produce_decoded().await?,
timestamp: cu64_decode_standalone(producer)
.await
.map_err(|err| err.map_other(|_| unreachable!()))?
.into(),
payload_length: cu64_decode_standalone(producer)
.await
.map_err(|err| err.map_other(|_| unreachable!()))?,
payload_digest: producer
.produce_decoded_canonic()
.await
.map_err(|err| err.map_other(Into::into))?,
})
}
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> DecodableCanonic
for Entry<MCL, MCC, MPL, N, S, PD>
where
N: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>>,
S: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>>,
PD: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>>,
{
type ErrorCanonic = Blame;
async fn decode_canonic<P>(
producer: &mut P,
) -> Result<Self, DecodeError<P::Final, P::Error, Self::ErrorCanonic>>
where
P: BulkProducer<Item = u8> + ?Sized,
Self: Sized,
{
Ok(Self {
namespace_id: producer
.produce_decoded_canonic()
.await
.map_err(|err| err.map_other(Into::into))?,
subspace_id: producer
.produce_decoded_canonic()
.await
.map_err(|err| err.map_other(Into::into))?,
path: producer.produce_decoded_canonic().await?,
timestamp: cu64_decode_canonic_standalone(producer)
.await
.map_err(|err| err.map_other(|_| unreachable!()))?
.into(),
payload_length: cu64_decode_canonic_standalone(producer)
.await
.map_err(|err| err.map_other(|_| unreachable!()))?,
payload_digest: producer
.produce_decoded_canonic()
.await
.map_err(|err| err.map_other(Into::into))?,
})
}
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, State: entry_builder::State>
EntryBuilder<MCL, MCC, MPL, N, S, PD, State>
{
#[cfg(feature = "std")]
pub fn now(
self,
) -> Result<
EntryBuilder<MCL, MCC, MPL, N, S, PD, entry_builder::SetTimestamp<State>>,
HifitimeError,
> {
Timestamp::now().map(|ts| self.timestamp(ts))
}
pub fn payload<Payload, H, Digest>(
self,
payload: Payload,
) -> EntryBuilder<
MCL,
MCC,
MPL,
N,
S,
PD,
entry_builder::SetPayloadLength<entry_builder::SetPayloadDigest<State>>,
>
where
Payload: AsRef<[u8]>,
H: Default + Hasher<Digest>,
Digest: Into<PD>,
{
let mut hasher = H::default();
hasher.write(payload.as_ref());
let digest = hasher.finish().into();
let length = u64::try_from(payload.as_ref().len()).expect("payload too long");
self.payload_digest(digest).payload_length(length)
}
pub async fn payload_async<P, H, Digest>(
self,
payload_producer: &mut P,
) -> Result<
EntryBuilder<
MCL,
MCC,
MPL,
N,
S,
PD,
entry_builder::SetPayloadLength<entry_builder::SetPayloadDigest<State>>,
>,
P::Error,
>
where
P: BulkProducer<Item = u8, Final = ()>,
H: Default + Hasher<Digest>,
Digest: Into<PD>,
{
let mut hasher = H::default();
let mut payload_len = 0;
loop {
match payload_producer
.expose_items_sync(|partial_payload| {
hasher.write(partial_payload);
payload_len += partial_payload.len();
(partial_payload.len(), ())
})
.await?
{
Either::Left(_) => {}
Either::Right(_) => {
let payload_digest = hasher.finish().into();
let payload_length = u64::try_from(payload_len).expect("payload too long");
return Ok(self
.payload_digest(payload_digest)
.payload_length(payload_length));
}
}
}
}
}