extern crate alloc;
use crate::metis::{Cut, DotSet, Dotted, Metatheses, Rhapsody, VersionVector};
use super::shadow::{EpochStratum, EpochStratumError};
use super::{Adopted, Declaration, EpochAddress};
use crate::metis::dot::RawDot;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[must_use = "a window entry was not folded; it belongs to the shadow replay after adoption"]
pub enum EpochPreparationFold {
Staged,
Window {
dot: RawDot,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
pub enum EpochPreparationMiss {
#[error("the seeded base already reaches {dot}, above the declared cut")]
UnpinnedBase {
dot: RawDot,
},
#[error("adoption fixed {adopted:?}, not the staged {staged:?}")]
DifferentWinner {
staged: EpochAddress,
adopted: EpochAddress,
},
#[error("the adopted cut disagrees with the staged cut at station {station}")]
DriftedCut {
station: u32,
staged: u64,
adopted: u64,
},
#[error("the staged pairs are not the adopted stratum: {0}")]
Stratum(#[source] EpochStratumError),
}
#[derive(Debug)]
pub struct EpochPreparation {
address: EpochAddress,
cut: Cut,
text: Dotted<Rhapsody>,
moves: Dotted<Metatheses>,
}
impl EpochPreparation {
pub fn begin(
declaration: &Declaration,
text: Dotted<Rhapsody>,
moves: Dotted<Metatheses>,
) -> Result<Self, EpochPreparationMiss> {
let cut = declaration.cut();
if let Some(dot) = beyond(cut.as_vector(), text.context())
.or_else(|| beyond(cut.as_vector(), text.store().woven()))
.or_else(|| beyond(cut.as_vector(), moves.context()))
{
return Err(EpochPreparationMiss::UnpinnedBase { dot });
}
Ok(Self {
address: declaration.address(),
cut: cut.clone(),
text,
moves,
})
}
#[must_use]
pub const fn address(&self) -> EpochAddress {
self.address
}
#[must_use]
pub const fn cut(&self) -> &Cut {
&self.cut
}
pub fn absorb_text(
&mut self,
events: &[RawDot],
delta: &Dotted<Rhapsody>,
) -> EpochPreparationFold {
let vector = self.cut.as_vector();
if let Some(dot) = Self::window_events(vector, events)
.or_else(|| beyond(vector, delta.context()))
.or_else(|| beyond(vector, delta.store().woven()))
{
return EpochPreparationFold::Window { dot };
}
self.text.merge_from(delta);
EpochPreparationFold::Staged
}
pub fn absorb_moves(
&mut self,
events: &[RawDot],
delta: &Dotted<Metatheses>,
) -> EpochPreparationFold {
let vector = self.cut.as_vector();
if let Some(dot) =
Self::window_events(vector, events).or_else(|| beyond(vector, delta.context()))
{
return EpochPreparationFold::Window { dot };
}
self.moves.merge_from(delta);
EpochPreparationFold::Staged
}
fn window_events(cut: &VersionVector, events: &[RawDot]) -> Option<RawDot> {
if events.is_empty() {
return Some(RawDot::new(0, 0));
}
events
.iter()
.copied()
.find(|raw| raw.counter == 0 || raw.counter > cut.get(raw.station))
}
pub fn finish(self, adopted: &Adopted) -> Result<EpochStratum, EpochPreparationMiss> {
if adopted.address() != self.address {
return Err(EpochPreparationMiss::DifferentWinner {
staged: self.address,
adopted: adopted.address(),
});
}
let staged = self.cut.as_vector();
let winner = adopted.cut().as_vector();
let drifted = staged
.into_iter()
.chain(winner)
.map(|(station, _)| station)
.find(|&station| staged.get(station) != winner.get(station));
if let Some(station) = drifted {
return Err(EpochPreparationMiss::DriftedCut {
station,
staged: staged.get(station),
adopted: winner.get(station),
});
}
EpochStratum::new(adopted, self.text, self.moves).map_err(EpochPreparationMiss::Stratum)
}
}
fn beyond(cut: &VersionVector, context: &DotSet) -> Option<RawDot> {
context
.high_water()
.into_iter()
.find(|&(station, high)| high > cut.get(station))
.map(RawDot::from)
}