minerva 0.2.0

Causal ordering for distributed systems
use core::fmt;
use core::marker::PhantomData;

use crate::kairos::Kairos;
use crate::metis::{Event, Gate};

/// An event released by an [`Ideal`](super::Ideal) through gate `G`.
///
/// On normal return, the machine's final readiness check admitted the contained
/// pending occurrence, the machine removed it, and [`Gate::advance`] returned for
/// that same occurrence. The private constructor makes this provenance a safe-code
/// invariant. `G` names the rule that participated; its dependency representation
/// alone is insufficient because distinct gates may share one [`Gate::Dep`].
///
/// The witness is neither [`Clone`] nor [`Copy`]. Consuming APIs can therefore use
/// it at most once in safe code, while dropping it remains valid. It does not prove
/// that a caller-defined gate satisfies its semantic laws, identify the originating
/// machine instance, carry caller eligibility policy, grant authority, establish
/// durability, or imply exactly-once external effects.
///
/// The gate brand is invariant and occupies no storage. `repr(transparent)` keeps
/// the witness's layout and ABI equal to its [`Event`] field.
///
/// A raw event cannot substitute for release provenance:
///
/// ```compile_fail
/// use minerva::metis::{Causal, Event, Released, VersionVector};
/// use minerva::kairos::Kairos;
///
/// fn needs_release(_: Released<u8, Causal>) {}
///
/// let event = Event {
///     stamp: Kairos::new(1, 0, 1, 0u16),
///     deps: VersionVector::new(),
///     payload: 1u8,
/// };
/// needs_release(event);
/// ```
///
/// Downstream code cannot construct the witness directly:
///
/// ```compile_fail
/// use core::marker::PhantomData;
/// use minerva::kairos::Kairos;
/// use minerva::metis::{Causal, Event, Released, VersionVector};
///
/// let event = Event {
///     stamp: Kairos::new(1, 0, 1, 0u16),
///     deps: VersionVector::new(),
///     payload: 1u8,
/// };
/// let _ = Released::<_, Causal> {
///     event,
///     _gate: PhantomData,
/// };
/// ```
///
/// Gate brands cannot be exchanged even when their dependency representations agree:
///
/// ```compile_fail
/// use minerva::metis::{Gate, Released};
///
/// struct Rule<const ID: u8>;
///
/// impl<const ID: u8> Gate for Rule<ID> {
///     type Dep = u64;
///     type Progress = ();
///
///     fn deliverable(_progress: &(), _sender: u32, _dep: &u64) -> bool {
///         false
///     }
///
///     fn advance(_progress: &mut (), _sender: u32, _dep: &u64) {}
///
///     fn stale(_progress: &(), _sender: u32, _dep: &u64) -> bool {
///         false
///     }
/// }
///
/// fn needs_rule_two(_: Released<u8, Rule<2>>) {}
///
/// fn wrong_gate(released: Released<u8, Rule<1>>) {
///     needs_rule_two(released);
/// }
/// ```
///
/// The provenance witness cannot be duplicated:
///
/// ```compile_fail
/// use minerva::metis::{Causal, Released};
///
/// fn duplicate(released: Released<u8, Causal>) {
///     let _copy = released.clone();
/// }
/// ```
#[repr(transparent)]
pub struct Released<T, G: Gate> {
    event: Event<T, G::Dep>,
    _gate: PhantomData<fn(G) -> G>,
}

impl<T, G: Gate> Released<T, G> {
    pub(super) const fn new(event: Event<T, G::Dep>) -> Self {
        Self {
            event,
            _gate: PhantomData,
        }
    }

    /// Borrows the released event without erasing its provenance.
    #[must_use]
    pub const fn event(&self) -> &Event<T, G::Dep> {
        &self.event
    }

    /// The released event's ordered identity.
    #[must_use]
    pub const fn stamp(&self) -> Kairos {
        self.event.stamp
    }

    /// The dependency descriptor admitted by gate `G`.
    #[must_use]
    pub const fn deps(&self) -> &G::Dep {
        &self.event.deps
    }

    /// The released payload.
    #[must_use]
    pub const fn payload(&self) -> &T {
        &self.event.payload
    }

    /// Erases release provenance and returns the raw event.
    #[must_use]
    pub fn into_event(self) -> Event<T, G::Dep> {
        self.event
    }

    /// Erases release provenance and returns the payload.
    #[must_use]
    pub fn into_payload(self) -> T {
        self.event.payload
    }
}

impl<T, G> fmt::Debug for Released<T, G>
where
    T: fmt::Debug,
    G: Gate,
    G::Dep: fmt::Debug,
{
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter
            .debug_tuple("Released")
            .field(&self.event)
            .finish()
    }
}