minerva 0.2.0

Causal ordering for distributed systems
use core::cmp::Ordering;
use core::marker::PhantomData;

use super::Scoped;
use crate::metis::{ScopedPeek, VersionVector};

impl Scoped<'_, VersionVector> {
    /// The scoped causal verdict: [`VersionVector::partial_cmp`] on the two
    /// restrictions.
    ///
    /// Sound *within* the scope by PRD 0013's preservation law (if `a <= b`
    /// globally then the restrictions agree), and inexpressible across scopes
    /// by the brand. The polarity is the whole point: a `Some(Less)` here means
    /// order *over the roster*, never order tout court, because restriction can
    /// drop exactly the stations that witnessed a disagreement. Acting on this
    /// verdict globally is the created-order hazard; for the verdict that does
    /// escape lawfully, see [`concurrent_globally`](Self::concurrent_globally).
    #[must_use]
    pub fn partial_cmp_scoped(&self, other: &Self) -> Option<Ordering> {
        self.value.partial_cmp(&other.value)
    }

    /// Whether this restriction strictly precedes `other`'s over the roster.
    ///
    /// A scoped order verdict: sound within the scope, meaningless outside it.
    /// This is order *over the roster*, never order tout court (PRD 0013); do
    /// not act on it globally.
    #[must_use]
    pub fn happens_before(&self, other: &Self) -> bool {
        self.value.happens_before(&other.value)
    }

    /// Whether the two restrictions are concurrent, a verdict that lawfully
    /// escapes the scope unbranded.
    ///
    /// Concurrency *reflects* globally (PRD 0013): concurrent restrictions
    /// certify concurrent originals, because the two leading stations survive
    /// in the roster. So unlike the order reads this returns a plain `bool`
    /// with no brand: a caller may carry it out of the scope and act on it
    /// globally. The converse still fails (concurrent originals may restrict to
    /// ordered ones), which is exactly why the *order* reads stay branded.
    #[must_use]
    pub fn concurrent_globally(&self, other: &Self) -> bool {
        self.value.concurrent(&other.value)
    }

    /// The scoped join: [`VersionVector::merge`] of the two restrictions,
    /// staying in the scope.
    ///
    /// Restriction is a lattice homomorphism (PRD 0013), so folding
    /// restrictions equals restricting the fold; the result is branded, so it
    /// never leaves the scope by accident.
    #[must_use]
    pub fn merge(&self, other: &Self) -> Self {
        Self {
            value: self.value.merge(&other.value),
            _brand: PhantomData,
        }
    }

    /// The scoped meet: [`VersionVector::meet`] of the two restrictions,
    /// staying in the scope.
    ///
    /// The order dual of [`merge`](Self::merge); restriction commutes with the
    /// meet exactly (PRD 0013), so the fold stays scoped.
    #[must_use]
    pub fn meet(&self, other: &Self) -> Self {
        Self {
            value: self.value.meet(&other.value),
            _brand: PhantomData,
        }
    }

    /// Leaves the scope, forgetting where the verdicts were valid.
    ///
    /// The named escape: dropping the brand returns the bare
    /// [`VersionVector`], and with it any memory of the roster the evidence was
    /// read over. The created-order hazard is exactly acting on a scoped order
    /// verdict globally, so the escape is named for what it costs. Concurrency
    /// verdicts do not need this door; [`concurrent_globally`](Self::concurrent_globally)
    /// already hands back an unbranded `bool`.
    #[must_use]
    pub fn forget(self) -> VersionVector {
        self.value
    }

    /// A borrowed, display-only read of the restriction, without escaping the
    /// brand and without a reachable order.
    ///
    /// # Why a wrapper, not `&VersionVector`
    ///
    /// A bare `&VersionVector` reopens the exact hole the brand closes:
    /// [`VersionVector`] is [`PartialOrd`], so `scope.restrict(&a).peek() <=
    /// scope.restrict(&b).peek()` would quietly yield a *scoped* order verdict
    /// through an *unbranded* reference, the created-order hazard walking out
    /// the display door (PRD 0013). [`ScopedPeek`] carries the reference for
    /// [`Debug`](core::fmt::Debug) rendering only: it has no [`PartialOrd`], no
    /// [`Deref`](core::ops::Deref), and no accessor handing the inner reference
    /// back, so the operators cannot be reached and the sound scoped order stays
    /// where it belongs, in [`partial_cmp_scoped`](Self::partial_cmp_scoped) and
    /// [`happens_before`](Self::happens_before). Two peeks compare *equal*
    /// ([`PartialEq`]) but never *ordered*: equality carries no roster-forgetting
    /// hazard, order does.
    #[must_use]
    pub const fn peek(&self) -> ScopedPeek<'_, VersionVector> {
        ScopedPeek::new(&self.value)
    }
}