extern crate alloc;
mod codec;
mod error;
mod state;
use alloc::collections::BTreeSet;
use core::num::NonZeroUsize;
pub use error::{EpochLedgerDecodeBudget, EpochLedgerDecodeError, EpochLedgerRehydrateError};
use state::SnapshotState;
use super::Epochs;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EpochLedgerSnapshot {
state: SnapshotState,
}
impl EpochLedgerSnapshot {
#[must_use]
pub const fn generation(&self) -> u64 {
self.state.generation.get()
}
pub fn roster(&self) -> impl Iterator<Item = u32> + '_ {
self.state.roster.iter().copied()
}
#[must_use]
pub const fn horizon(&self) -> u64 {
self.state.horizon
}
#[must_use]
pub const fn has_open_window(&self) -> bool {
self.state.window.is_some()
}
#[must_use]
pub const fn sealed_len(&self) -> usize {
self.state.lineage.len()
}
#[must_use]
pub fn candidate_len(&self) -> usize {
self.state
.window
.as_ref()
.map_or(0, |window| window.candidates.len())
}
}
impl Epochs {
#[must_use]
pub fn snapshot(&self) -> EpochLedgerSnapshot {
EpochLedgerSnapshot {
state: SnapshotState::from_epochs(self),
}
}
pub fn rehydrate(
snapshot: &EpochLedgerSnapshot,
roster: impl IntoIterator<Item = u32>,
horizon: NonZeroUsize,
) -> Result<Self, EpochLedgerRehydrateError> {
let roster: BTreeSet<u32> = roster.into_iter().collect();
if roster != snapshot.state.roster {
return Err(EpochLedgerRehydrateError::RosterMismatch);
}
let configured = u64::try_from(horizon.get()).unwrap_or(u64::MAX);
if configured != snapshot.state.horizon {
return Err(EpochLedgerRehydrateError::HorizonMismatch {
checkpoint: snapshot.state.horizon,
configured,
});
}
Ok(snapshot.state.to_epochs(horizon))
}
}