use core::num::NonZeroU64;
use crate::metis::dot::Dot;
use crate::metis::{Adopted, Gate, Ideal};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EpochAddress {
generation: NonZeroU64,
declaration: Dot,
}
impl EpochAddress {
pub(super) const fn new(generation: NonZeroU64, declaration: Dot) -> Self {
Self {
generation,
declaration,
}
}
#[must_use]
pub const fn generation(self) -> u64 {
self.generation.get()
}
#[must_use]
pub const fn declaration(self) -> Dot {
self.declaration
}
pub const fn try_from_parts(
generation: u64,
declaration: (u32, u64),
) -> Result<Self, InvalidEpochAddress> {
let Some(generation) = NonZeroU64::new(generation) else {
return Err(InvalidEpochAddress::ZeroGeneration);
};
match Dot::from_parts(declaration.0, declaration.1) {
Ok(declaration) => Ok(Self {
generation,
declaration,
}),
Err(_) => Err(InvalidEpochAddress::ZeroDeclarationCounter {
station: declaration.0,
}),
}
}
}
impl core::fmt::Display for EpochAddress {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"({}, {}, {})",
self.generation,
self.declaration.station(),
self.declaration.counter()
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
pub enum InvalidEpochAddress {
#[error("epoch generation zero is invalid")]
ZeroGeneration,
#[error("epoch declaration at station {station} has the non-dot counter zero")]
ZeroDeclarationCounter {
station: u32,
},
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn durable_address_parts_rehydrate_only_when_generation_and_counter_are_nonzero() {
let address = EpochAddress::try_from_parts(7, (3, 11)).unwrap();
assert_eq!(address.generation(), 7);
assert_eq!(address.declaration(), Dot::from_parts(3, 11).unwrap());
assert_eq!(
EpochAddress::try_from_parts(0, (3, 11)),
Err(InvalidEpochAddress::ZeroGeneration)
);
assert_eq!(
EpochAddress::try_from_parts(7, (3, 0)),
Err(InvalidEpochAddress::ZeroDeclarationCounter { station: 3 })
);
let wide = EpochAddress::try_from_parts(7, (0, 1)).unwrap();
assert_eq!(wide.declaration().station(), 0);
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct EpochGate;
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
#[error("epoch buffer already adopted {adopted:?}; cannot adopt {requested:?}")]
pub struct EpochAdoptionMismatch {
pub adopted: EpochAddress,
pub requested: EpochAddress,
}
impl Gate for EpochGate {
type Dep = EpochAddress;
type Progress = Option<EpochAddress>;
fn deliverable(progress: &Self::Progress, _sender: u32, dep: &Self::Dep) -> bool {
progress.as_ref() == Some(dep)
}
fn advance(progress: &mut Self::Progress, _sender: u32, dep: &Self::Dep) {
if progress.is_none() {
*progress = Some(*dep);
}
}
fn stale(progress: &Self::Progress, _sender: u32, dep: &Self::Dep) -> bool {
progress.is_some_and(|adopted| adopted != *dep)
}
}
impl<T> Ideal<T, EpochGate> {
pub fn adopt_epoch(&mut self, adopted: &Adopted) -> Result<(), EpochAdoptionMismatch> {
let requested = adopted.address();
if let Some(adopted) = *self.progress()
&& adopted != requested
{
return Err(EpochAdoptionMismatch { adopted, requested });
}
self.advance_progress(requested.declaration().station(), &requested);
Ok(())
}
}