#[cfg(feature = "dev")]
use arbitrary::{size_hint::and_all, Arbitrary};
use crate::{
parameters::{AuthorisationToken, NamespaceId, PayloadDigest, SubspaceId},
path::Path,
};
pub type Timestamp = u64;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Entry<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
where
N: NamespaceId,
S: SubspaceId,
PD: PayloadDigest,
{
namespace_id: N,
subspace_id: S,
path: Path<MCL, MCC, MPL>,
timestamp: Timestamp,
payload_length: u64,
payload_digest: PD,
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Entry<MCL, MCC, MPL, N, S, PD>
where
N: NamespaceId,
S: SubspaceId,
PD: PayloadDigest,
{
pub fn new(
namespace_id: N,
subspace_id: S,
path: Path<MCL, MCC, MPL>,
timestamp: Timestamp,
payload_length: u64,
payload_digest: PD,
) -> Self {
Entry {
namespace_id,
subspace_id,
path,
timestamp,
payload_length,
payload_digest,
}
}
pub fn namespace_id(&self) -> &N {
&self.namespace_id
}
pub fn subspace_id(&self) -> &S {
&self.subspace_id
}
pub fn path(&self) -> &Path<MCL, MCC, MPL> {
&self.path
}
pub fn timestamp(&self) -> Timestamp {
self.timestamp
}
pub fn payload_length(&self) -> u64 {
self.payload_length
}
pub fn payload_digest(&self) -> &PD {
&self.payload_digest
}
pub fn is_newer_than(&self, other: &Self) -> bool {
other.timestamp < self.timestamp
|| (other.timestamp == self.timestamp && other.payload_digest < self.payload_digest)
|| (other.timestamp == self.timestamp
&& other.payload_digest == self.payload_digest
&& other.payload_length < self.payload_length)
}
}
use syncify::syncify;
use syncify::syncify_replace;
#[syncify(encoding_sync)]
mod encoding {
use super::*;
#[syncify_replace(use ufotofu::sync::{BulkConsumer, BulkProducer};)]
use ufotofu::local_nb::{BulkConsumer, BulkProducer};
use willow_encoding::{DecodeError, U64BE};
#[syncify_replace(use willow_encoding::sync::{Decodable, Encodable};)]
use willow_encoding::{Decodable, Encodable};
impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Encodable
for Entry<MCL, MCC, MPL, N, S, PD>
where
N: NamespaceId + Encodable,
S: SubspaceId + Encodable,
PD: PayloadDigest + Encodable,
{
async fn encode<C>(&self, consumer: &mut C) -> Result<(), <C>::Error>
where
C: BulkConsumer<Item = u8>,
{
self.namespace_id.encode(consumer).await?;
self.subspace_id.encode(consumer).await?;
self.path.encode(consumer).await?;
U64BE::from(self.timestamp).encode(consumer).await?;
U64BE::from(self.payload_length).encode(consumer).await?;
self.payload_digest.encode(consumer).await?;
Ok(())
}
}
impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Decodable
for Entry<MCL, MCC, MPL, N, S, PD>
where
N: NamespaceId + Decodable,
S: SubspaceId + Decodable,
PD: PayloadDigest + Decodable,
{
async fn decode<Prod>(producer: &mut Prod) -> Result<Self, DecodeError<Prod::Error>>
where
Prod: BulkProducer<Item = u8>,
{
let namespace_id = N::decode(producer).await?;
let subspace_id = S::decode(producer).await?;
let path = Path::<MCL, MCC, MPL>::decode(producer).await?;
let timestamp = U64BE::decode(producer).await?.into();
let payload_length = U64BE::decode(producer).await?.into();
let payload_digest = PD::decode(producer).await?;
Ok(Entry {
namespace_id,
subspace_id,
path,
timestamp,
payload_length,
payload_digest,
})
}
}
}
#[cfg(feature = "dev")]
impl<'a, const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Arbitrary<'a>
for Entry<MCL, MCC, MPL, N, S, PD>
where
N: NamespaceId + Arbitrary<'a>,
S: SubspaceId + Arbitrary<'a>,
PD: PayloadDigest + Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let namespace_id: N = Arbitrary::arbitrary(u)?;
let subspace_id: S = Arbitrary::arbitrary(u)?;
let path: Path<MCL, MCC, MPL> = Arbitrary::arbitrary(u)?;
let payload_digest: PD = Arbitrary::arbitrary(u)?;
Ok(Self {
namespace_id,
subspace_id,
path,
payload_digest,
payload_length: Arbitrary::arbitrary(u)?,
timestamp: Arbitrary::arbitrary(u)?,
})
}
fn size_hint(depth: usize) -> (usize, Option<usize>) {
and_all(&[
N::size_hint(depth),
S::size_hint(depth),
Path::<MCL, MCC, MPL>::size_hint(depth),
PD::size_hint(depth),
u64::size_hint(depth),
u64::size_hint(depth),
])
}
}
#[derive(Debug)]
pub struct UnauthorisedWriteError;
impl core::fmt::Display for UnauthorisedWriteError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Tried to authorise the writing of an entry using an AuthorisationToken which does not permit it."
)
}
}
impl std::error::Error for UnauthorisedWriteError {}
pub struct AuthorisedEntry<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT>(
pub Entry<MCL, MCC, MPL, N, S, PD>,
pub AT,
)
where
N: NamespaceId,
S: SubspaceId,
PD: PayloadDigest;
impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD, AT>
AuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT>
where
N: NamespaceId,
S: SubspaceId,
PD: PayloadDigest,
{
pub fn new(
entry: Entry<MCL, MCC, MPL, N, S, PD>,
token: AT,
) -> Result<Self, UnauthorisedWriteError>
where
AT: AuthorisationToken<MCL, MCC, MPL, N, S, PD>,
{
if token.is_authorised_write(&entry) {
return Ok(Self(entry, token));
}
Err(UnauthorisedWriteError)
}
}
#[cfg(test)]
mod tests {
use crate::path::Component;
use super::*;
#[derive(Default, PartialEq, Eq, Clone)]
struct FakeNamespaceId(usize);
impl NamespaceId for FakeNamespaceId {}
#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Clone)]
struct FakeSubspaceId(usize);
impl SubspaceId for FakeSubspaceId {
fn successor(&self) -> Option<Self> {
Some(FakeSubspaceId(self.0 + 1))
}
}
#[derive(Default, PartialEq, Eq, PartialOrd, Ord, Clone)]
struct FakePayloadDigest(usize);
impl PayloadDigest for FakePayloadDigest {}
const MCL: usize = 8;
const MCC: usize = 4;
const MPL: usize = 16;
#[test]
fn entry_newer_than() {
let e_a1 = Entry {
namespace_id: FakeNamespaceId::default(),
subspace_id: FakeSubspaceId::default(),
path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(b"a").unwrap()]).unwrap(),
payload_digest: FakePayloadDigest::default(),
payload_length: 0,
timestamp: 20,
};
let e_a2 = Entry {
namespace_id: FakeNamespaceId::default(),
subspace_id: FakeSubspaceId::default(),
path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(b"a").unwrap()]).unwrap(),
payload_digest: FakePayloadDigest::default(),
payload_length: 0,
timestamp: 10,
};
assert!(e_a1.is_newer_than(&e_a2));
let e_b1 = Entry {
namespace_id: FakeNamespaceId::default(),
subspace_id: FakeSubspaceId::default(),
path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(b"a").unwrap()]).unwrap(),
payload_digest: FakePayloadDigest(2),
payload_length: 0,
timestamp: 10,
};
let e_b2 = Entry {
namespace_id: FakeNamespaceId::default(),
subspace_id: FakeSubspaceId::default(),
path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(b"a").unwrap()]).unwrap(),
payload_digest: FakePayloadDigest(1),
payload_length: 0,
timestamp: 10,
};
assert!(e_b1.is_newer_than(&e_b2));
let e_c1 = Entry {
namespace_id: FakeNamespaceId::default(),
subspace_id: FakeSubspaceId::default(),
path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(b"a").unwrap()]).unwrap(),
payload_digest: FakePayloadDigest::default(),
payload_length: 2,
timestamp: 20,
};
let e_c2 = Entry {
namespace_id: FakeNamespaceId::default(),
subspace_id: FakeSubspaceId::default(),
path: Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(b"a").unwrap()]).unwrap(),
payload_digest: FakePayloadDigest::default(),
payload_length: 1,
timestamp: 20,
};
assert!(e_c1.is_newer_than(&e_c2));
}
}