hibana 0.8.0

Const-projected Affine Multiparty Session Types for choreography-first Rust protocols
Documentation
//! Typed offer route-shape profile and ingress planning.

use super::{OfferScopeSelection, RouteDecisionSource, ingress::OfferIngressMode};

mod evidence;
mod planning;

pub(super) use self::evidence::{
    OfferArmRecvEvidence, OfferControllerArmEntry, OfferControllerCursorArm,
    OfferControllerSkipEvidence, OfferMaterializationReadiness, OfferPassiveAckEvidence,
    OfferPassiveEvidence, OfferPassiveReadySignal, OfferPassiveRecvEvidence,
};

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::endpoint::kernel) enum OfferScopeProfile {
    ControllerStatic,
    ControllerDynamic,
    PassiveStatic,
    PassiveDynamic,
}

#[derive(Clone, Copy, Eq, PartialEq)]
pub(super) enum OfferAuthorityRole {
    Controller,
    Passive,
}

#[derive(Clone, Copy, Eq, PartialEq)]
pub(super) enum OfferRouteScopeKind {
    Static,
    Dynamic,
}

#[derive(Clone, Copy, Eq, PartialEq)]
pub(super) enum OfferAuthorityPath {
    ControllerResolver,
    PassiveEvidence,
    LocalSources,
}

#[derive(Clone, Copy)]
pub(super) enum OfferEntryPosition {
    RouteEntry,
    AfterRouteEntry,
}

impl OfferEntryPosition {
    #[inline]
    pub(super) const fn from_route_entry(at_route_entry: bool) -> Self {
        if at_route_entry {
            Self::RouteEntry
        } else {
            Self::AfterRouteEntry
        }
    }

    #[inline]
    const fn is_after_route_entry(self) -> bool {
        matches!(self, Self::AfterRouteEntry)
    }
}

#[derive(Clone, Copy)]
pub(super) enum OfferCursorReadiness {
    Recv,
    NonRecv,
}

#[derive(Clone, Copy)]
pub(super) enum OfferEarlyDecisionReadiness {
    Unavailable,
    AvailableWithRecv,
    AvailableWithoutRecv,
}

impl OfferEarlyDecisionReadiness {
    #[inline]
    const fn available(self) -> bool {
        !matches!(self, Self::Unavailable)
    }

    #[inline]
    const fn arm_has_no_recv(self) -> bool {
        matches!(self, Self::AvailableWithoutRecv)
    }
}

#[derive(Clone, Copy)]
pub(super) enum OfferControllerSkipReadiness {
    Ready,
    BlockedByMaterialization,
    NeedsTransport,
}

#[derive(Clone, Copy)]
pub(super) enum OfferControllerRecvStep {
    SelectedPeerRecv,
    NeedsTransportRecv,
}

impl OfferControllerRecvStep {
    #[inline]
    const fn can_probe_binding(self) -> bool {
        matches!(self, Self::SelectedPeerRecv)
    }
}

#[derive(Clone, Copy)]
pub(super) struct OfferControllerReadiness {
    pub(super) recv_step: OfferControllerRecvStep,
    pub(super) skip: OfferControllerSkipReadiness,
}

#[derive(Clone, Copy)]
pub(super) enum OfferPassiveReadiness {
    ReadyArmOrFrameHint,
    DynamicScopeWithoutRecv,
    DynamicAckMaterializable,
    NeedsTransport,
}

#[derive(Clone, Copy)]
pub(super) enum OfferBindingProbeMode {
    SelectedBinding,
    SelectedAndRecvlessLoopBinding,
}

impl OfferBindingProbeMode {
    #[inline]
    const fn ingress_mode(self) -> OfferIngressMode {
        match self {
            Self::SelectedBinding => OfferIngressMode::ProbeSelectedBinding,
            Self::SelectedAndRecvlessLoopBinding => {
                OfferIngressMode::ProbeSelectedAndRecvlessLoopBinding
            }
        }
    }
}

#[derive(Clone, Copy)]
pub(super) struct OfferRouteShape {
    pub(super) profile: OfferScopeProfile,
    pub(super) entry: OfferEntryPosition,
    pub(super) cursor: OfferCursorReadiness,
    pub(super) early_decision: OfferEarlyDecisionReadiness,
    pub(super) controller: OfferControllerReadiness,
    pub(super) passive: OfferPassiveReadiness,
    pub(super) binding_probe: OfferBindingProbeMode,
}

impl OfferRouteShape {
    #[inline]
    pub(super) const fn ingress_mode(self) -> OfferIngressMode {
        match self.profile {
            OfferScopeProfile::ControllerStatic | OfferScopeProfile::ControllerDynamic => {
                self.controller_ingress_mode()
            }
            OfferScopeProfile::PassiveStatic | OfferScopeProfile::PassiveDynamic => {
                self.passive_ingress_mode()
            }
        }
    }

    #[inline]
    const fn controller_ingress_mode(self) -> OfferIngressMode {
        if self.controller_can_skip_recv() || self.early_decision.arm_has_no_recv() {
            return OfferIngressMode::Skip;
        }
        if self.controller.recv_step.can_probe_binding() {
            self.binding_probe_mode()
        } else {
            OfferIngressMode::TransportOnly
        }
    }

    #[inline]
    const fn passive_ingress_mode(self) -> OfferIngressMode {
        if self.passive_can_skip_recv() || self.early_decision.arm_has_no_recv() {
            return OfferIngressMode::Skip;
        }
        self.binding_probe_mode()
    }

    #[inline]
    const fn binding_probe_mode(self) -> OfferIngressMode {
        self.binding_probe.ingress_mode()
    }

    #[inline]
    const fn controller_can_skip_recv(self) -> bool {
        if matches!(
            self.controller.skip,
            OfferControllerSkipReadiness::BlockedByMaterialization
        ) {
            return false;
        }
        match (self.entry, self.cursor) {
            (OfferEntryPosition::RouteEntry, _) => {
                self.profile.is_dynamic()
                    || matches!(self.controller.skip, OfferControllerSkipReadiness::Ready)
                    || self.early_decision.available()
            }
            (OfferEntryPosition::AfterRouteEntry, OfferCursorReadiness::NonRecv) => true,
            (OfferEntryPosition::AfterRouteEntry, OfferCursorReadiness::Recv) => false,
        }
    }

    #[inline]
    const fn passive_can_skip_recv(self) -> bool {
        matches!(
            self.passive,
            OfferPassiveReadiness::ReadyArmOrFrameHint
                | OfferPassiveReadiness::DynamicScopeWithoutRecv
                | OfferPassiveReadiness::DynamicAckMaterializable
        )
    }
}

impl OfferScopeProfile {
    #[inline]
    pub(super) const fn from_evidence(role: OfferAuthorityRole, kind: OfferRouteScopeKind) -> Self {
        match (role, kind) {
            (OfferAuthorityRole::Controller, OfferRouteScopeKind::Dynamic) => {
                Self::ControllerDynamic
            }
            (OfferAuthorityRole::Controller, OfferRouteScopeKind::Static) => Self::ControllerStatic,
            (OfferAuthorityRole::Passive, OfferRouteScopeKind::Dynamic) => Self::PassiveDynamic,
            (OfferAuthorityRole::Passive, OfferRouteScopeKind::Static) => Self::PassiveStatic,
        }
    }

    #[inline]
    pub(super) const fn is_controller(self) -> bool {
        matches!(self, Self::ControllerStatic | Self::ControllerDynamic)
    }

    #[inline]
    pub(super) const fn is_passive(self) -> bool {
        !self.is_controller()
    }

    #[inline]
    pub(super) const fn is_dynamic(self) -> bool {
        matches!(self, Self::ControllerDynamic | Self::PassiveDynamic)
    }

    #[inline]
    pub(super) const fn suppresses_scope_frame_hint(self) -> bool {
        self.is_dynamic()
    }

    #[inline]
    pub(super) const fn recovers_frame_hint_conflict(self) -> bool {
        matches!(
            self,
            Self::ControllerDynamic | Self::PassiveStatic | Self::PassiveDynamic
        )
    }

    #[inline]
    pub(super) const fn authority_path_after_ack_miss(self) -> OfferAuthorityPath {
        match self {
            Self::ControllerDynamic => OfferAuthorityPath::ControllerResolver,
            Self::PassiveStatic | Self::PassiveDynamic => OfferAuthorityPath::PassiveEvidence,
            Self::ControllerStatic => OfferAuthorityPath::LocalSources,
        }
    }

    #[inline]
    pub(super) const fn transport_marks_ready_from_source(
        self,
        source: RouteDecisionSource,
    ) -> bool {
        match self {
            Self::PassiveDynamic => {
                matches!(source, RouteDecisionSource::Ack | RouteDecisionSource::Poll)
            }
            Self::ControllerDynamic => {
                matches!(
                    source,
                    RouteDecisionSource::Resolver | RouteDecisionSource::Poll
                )
            }
            Self::ControllerStatic | Self::PassiveStatic => false,
        }
    }

    #[inline]
    pub(super) const fn keeps_current_scope_for_unready_resolver(
        self,
        selection: OfferScopeSelection,
        source: RouteDecisionSource,
    ) -> bool {
        matches!(self, Self::ControllerDynamic)
            && !selection.at_route_offer_entry
            && matches!(source, RouteDecisionSource::Resolver)
    }

    #[inline]
    pub(super) const fn static_passive_progress_after_defer(self) -> bool {
        matches!(self, Self::PassiveStatic)
    }

    #[inline]
    pub(super) const fn poll_wire_commit_requires_event(self) -> bool {
        self.is_dynamic()
    }

    #[inline]
    pub(super) const fn poll_wire_commit_requires_static_observation(self) -> bool {
        !self.is_dynamic()
    }

    #[inline]
    pub(super) const fn publishes_recvless_parent_route_decision(self) -> bool {
        self.is_passive()
    }

    #[inline]
    pub(super) const fn publishes_controller_ack_decision(self) -> bool {
        self.is_controller()
    }
}