minerva 0.2.0

Causal ordering for distributed systems
//! The run reservation: `KairosRun`, the receipt of one bulk mint.
//!
//! A run mint ([`Clock::now_run`](super::Clock::now_run) /
//! [`LocalClock::now_run`](super::LocalClock::now_run)) reserves `len`
//! consecutive send positions in one commitment, and this type is what it
//! hands back: the first stamp plus the length, with every later member
//! *derived* by the fold's own frozen-reading send step (the successor rule
//! the snapshot codec shares as `rank_successor`, proven equal to the fold by
//! the S197 Kani harness). Nothing here is a second time semantics: a member
//! read is arithmetic over the reserved interval, and the clock has already
//! committed past the whole run, so members never collide with later mints.
//!
//! Construction is crate-private on purpose: within this crate the only
//! sources are the two minting shells, so holding a `KairosRun` is holding a
//! genuine reservation (the witness discipline the crate's `Cut` and
//! `Retired` types set). It is *affine*: neither `Copy` nor `Clone`, and the
//! consuming weave takes it by value, so a reservation is spent at most
//! once. Re-weaving one run under other identities would forge rank ties an
//! honest mint never produces, and the type makes that a move error rather
//! than a discipline. (`Kairos::new` being public means fabricated *stamps*
//! are always possible; the run type simply never launders a fabricated
//! *interval* as a reservation.)

use super::super::stamp::Kairos;
use super::fold;

/// A contiguous reservation of rank stamps: the first stamp and how many
/// consecutive send positions were reserved from it, all sharing one station
/// and one kairotic tag.
///
/// Member `i` (zero-based) is the first stamp advanced `i` send steps: the
/// logical component steps by one, rolling into the next physical unit at
/// the sentinel, exactly the succession an equal number of individual mints
/// against a frozen reading would have committed. Members therefore strictly
/// ascend and chain under the `metis` chain law (anchor-After, equal
/// kairotic, equal station, successor rank), which is what lets a woven run
/// coalesce by birthright in the identity plane, the snapshot codec, and the
/// order thread.
///
/// The strict-ascent guarantee shares the clock's documented saturation
/// edge: a run whose carry would cross the `u64::MAX` physical ceiling pins
/// there (never wraps), and domination past the pin is arithmetically
/// impossible. Every proof carries the same precondition the single mint's
/// proofs do.
///
/// Affine on purpose (no `Clone`, no `Copy`): the consuming weave
/// ([`Rhapsody::weave_run`](crate::metis::Rhapsody::weave_run)) takes the
/// reservation by value, so spending it twice is a compile error. A refused
/// weave consumes the reservation too; mint another (the clock only skips
/// positions, which is always lawful).
#[derive(Debug, PartialEq, Eq)]
#[must_use = "a dropped reservation burns its send positions"]
pub struct KairosRun {
    /// The run's first stamp.
    first: Kairos,
    /// How many consecutive send positions were reserved, at least one.
    len: u32,
}

// A reservation is never empty (`len` is at least one by construction), so
// there is deliberately no `is_empty` beside `len`.
#[allow(clippy::len_without_is_empty)]
impl KairosRun {
    /// Crate-private mint: only the clock shells construct reservations.
    /// `len` arrives from the shells' `NonZeroU32`, so it is at least one.
    pub(crate) const fn new(first: Kairos, len: u32) -> Self {
        Self { first, len }
    }

    /// The run's first stamp.
    #[must_use]
    pub const fn first(&self) -> Kairos {
        self.first
    }

    /// The run's last stamp: the pair the mint committed, which every
    /// subsequent mint of the same clock strictly dominates.
    #[must_use]
    pub fn last(&self) -> Kairos {
        let (physical, logical) = fold::rank_offset(
            self.first.physical(),
            self.first.logical(),
            u64::from(self.len.saturating_sub(1)),
        );
        Kairos::new(
            physical,
            logical,
            self.first.station_id(),
            self.first.kairotic(),
        )
    }

    /// How many stamps the run carries, at least one.
    #[must_use]
    pub const fn len(&self) -> u32 {
        self.len
    }

    /// Member `index` (zero-based), or `None` past the reservation.
    #[must_use]
    pub fn get(&self, index: u32) -> Option<Kairos> {
        if index >= self.len {
            return None;
        }
        let (physical, logical) = fold::rank_offset(
            self.first.physical(),
            self.first.logical(),
            u64::from(index),
        );
        Some(Kairos::new(
            physical,
            logical,
            self.first.station_id(),
            self.first.kairotic(),
        ))
    }

    /// The members in order, exactly `len` stamps (a borrowing read: the
    /// reservation stays spendable).
    #[must_use]
    pub const fn iter(&self) -> KairosRunIter<'_> {
        KairosRunIter { run: self, next: 0 }
    }
}

impl<'a> IntoIterator for &'a KairosRun {
    type Item = Kairos;
    type IntoIter = KairosRunIter<'a>;

    fn into_iter(self) -> KairosRunIter<'a> {
        self.iter()
    }
}

/// The member iterator of a [`KairosRun`]: exactly `len` stamps, in order.
#[derive(Clone, Debug)]
pub struct KairosRunIter<'a> {
    run: &'a KairosRun,
    next: u32,
}

impl Iterator for KairosRunIter<'_> {
    type Item = Kairos;

    fn next(&mut self) -> Option<Kairos> {
        let stamp = self.run.get(self.next)?;
        self.next += 1;
        Some(stamp)
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let remaining = (self.run.len - self.next) as usize;
        (remaining, Some(remaining))
    }
}

impl ExactSizeIterator for KairosRunIter<'_> {}

impl core::iter::FusedIterator for KairosRunIter<'_> {}