minerva 0.2.0

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

use super::{Gate, ReleaseDrivenGate};

/// The conjunction of two [`Gate`] rules.
///
/// An event is deliverable only when both gates admit it and terminally stale
/// when either gate rejects it forever. Release advances both component
/// progresses because every product release is a genuine release under each
/// component. Both components must be [`ReleaseDrivenGate`]s: a gate with
/// independently authorized progress, such as
/// [`EpochGate`](crate::metis::EpochGate), needs a product-aware checked API
/// before it can be composed safely.
///
/// ```compile_fail
/// use minerva::metis::{And, Causal, EpochGate, Ideal};
///
/// let _: Ideal<(), And<EpochGate, Causal>> = Ideal::default();
/// ```
pub struct And<A, B>(PhantomData<fn() -> (A, B)>);

// Hand-written marker impls: a derive would demand `A: Debug` (and so on)
// from components that are themselves phantom markers.
impl<A, B> core::fmt::Debug for And<A, B> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.write_str("And")
    }
}

impl<A, B> Clone for And<A, B> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<A, B> Copy for And<A, B> {}

impl<A, B> Default for And<A, B> {
    fn default() -> Self {
        Self(PhantomData)
    }
}

impl<A: ReleaseDrivenGate, B: ReleaseDrivenGate> Gate for And<A, B> {
    type Dep = (A::Dep, B::Dep);
    type Progress = AndProgress<A::Progress, B::Progress>;

    fn deliverable(progress: &Self::Progress, sender: u32, dep: &Self::Dep) -> bool {
        A::deliverable(progress.left(), sender, &dep.0)
            && B::deliverable(progress.right(), sender, &dep.1)
    }

    fn advance(progress: &mut Self::Progress, sender: u32, dep: &Self::Dep) {
        A::advance(&mut progress.left, sender, &dep.0);
        B::advance(&mut progress.right, sender, &dep.1);
    }

    fn stale(progress: &Self::Progress, sender: u32, dep: &Self::Dep) -> bool {
        A::stale(progress.left(), sender, &dep.0) || B::stale(progress.right(), sender, &dep.1)
    }
}

impl<A: ReleaseDrivenGate, B: ReleaseDrivenGate> ReleaseDrivenGate for And<A, B> {}

/// Monotone progress for [`And`], ordered componentwise.
///
/// Rust tuples compare lexicographically, which is not the product information
/// order required by a conjunctive gate. This nominal type makes crossed
/// progress incomparable instead of silently choosing the first component.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct AndProgress<A, B> {
    left: A,
    right: B,
}

impl<A, B> AndProgress<A, B> {
    /// Constructs product progress from its component states.
    #[must_use]
    pub const fn new(left: A, right: B) -> Self {
        Self { left, right }
    }

    /// Returns the left gate's progress.
    #[must_use]
    pub const fn left(&self) -> &A {
        &self.left
    }

    /// Returns the right gate's progress.
    #[must_use]
    pub const fn right(&self) -> &B {
        &self.right
    }

    /// Splits the product into its component states.
    #[must_use]
    pub fn into_parts(self) -> (A, B) {
        (self.left, self.right)
    }
}

impl<A: PartialOrd, B: PartialOrd> PartialOrd for AndProgress<A, B> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        use Ordering::{Equal, Greater, Less};

        match (
            self.left.partial_cmp(&other.left)?,
            self.right.partial_cmp(&other.right)?,
        ) {
            (Equal, ordering) | (ordering, Equal) => Some(ordering),
            (Less, Less) => Some(Less),
            (Greater, Greater) => Some(Greater),
            (Less, Greater) | (Greater, Less) => None,
        }
    }
}