hibana 0.8.0

Const-projected Affine Multiparty Session Types for choreography-first Rust protocols
Documentation
use super::super::super::evidence::ScopeLoopMeta;
use super::super::{
    Clock, CursorEndpoint, EndpointSlot, EpochTable, LabelUniverse, MintConfigMarker,
    OfferScopeProfile, OfferScopeSelection, RouteDecisionToken, ScopeArmMaterializationMeta,
    Transport,
    profile::{
        OfferArmRecvEvidence, OfferAuthorityRole, OfferBindingProbeMode, OfferControllerArmEntry,
        OfferControllerCursorArm, OfferControllerReadiness, OfferControllerRecvStep,
        OfferControllerSkipEvidence, OfferControllerSkipReadiness, OfferCursorReadiness,
        OfferEarlyDecisionReadiness, OfferEntryPosition, OfferMaterializationReadiness,
        OfferPassiveAckEvidence, OfferPassiveEvidence, OfferPassiveReadiness,
        OfferPassiveReadySignal, OfferPassiveRecvEvidence, OfferRouteScopeKind,
    },
};
use super::evidence::OfferIngressEvidence;

struct OfferIngressPlannerInput {
    selection: OfferScopeSelection,
    entry: OfferEntryPosition,
    loop_meta: ScopeLoopMeta,
    materialization: ScopeArmMaterializationMeta,
    preview_route_decision: Option<RouteDecisionToken>,
    cursor: OfferCursorReadiness,
}

impl<'r, const ROLE: u8, T, U, C, E, const MAX_RV: usize, Mint, B>
    CursorEndpoint<'r, ROLE, T, U, C, E, MAX_RV, Mint, B>
where
    T: Transport + 'r,
    U: LabelUniverse,
    C: Clock,
    E: EpochTable,
    Mint: MintConfigMarker,
    B: EndpointSlot + 'r,
{
    pub(super) fn offer_ingress_evidence(
        &mut self,
        selection: OfferScopeSelection,
        entry: OfferEntryPosition,
        profile: OfferScopeProfile,
        offer_lanes: crate::global::role_program::LaneSetView,
    ) -> OfferIngressEvidence {
        let scope_id = selection.scope_id;
        let offer_lane_idx = selection.offer_lane as usize;
        let input = OfferIngressPlannerInput {
            selection,
            entry,
            loop_meta: self.selection_frame_label_meta(selection).loop_meta(),
            materialization: self.selection_materialization_meta(selection),
            preview_route_decision: self.preview_scope_ack_token_non_consuming(
                scope_id,
                offer_lane_idx,
                offer_lanes,
            ),
            cursor: if self.cursor.is_recv() {
                OfferCursorReadiness::Recv
            } else {
                OfferCursorReadiness::NonRecv
            },
        };

        match profile {
            OfferScopeProfile::ControllerStatic => self.controller_static_ingress_evidence(input),
            OfferScopeProfile::ControllerDynamic => self.controller_dynamic_ingress_evidence(input),
            OfferScopeProfile::PassiveStatic => self.passive_static_ingress_evidence(input),
            OfferScopeProfile::PassiveDynamic => self.passive_dynamic_ingress_evidence(input),
        }
    }

    pub(super) fn offer_scope_profile(
        &self,
        scope_id: crate::global::const_dsl::ScopeId,
    ) -> OfferScopeProfile {
        let role = if self.cursor.is_route_controller(scope_id) {
            OfferAuthorityRole::Controller
        } else {
            OfferAuthorityRole::Passive
        };
        let kind = match self.cursor.route_scope_controller_policy(scope_id) {
            Some((policy, _, _, _)) if policy.is_dynamic() => OfferRouteScopeKind::Dynamic,
            _ => OfferRouteScopeKind::Static,
        };
        OfferScopeProfile::from_evidence(role, kind)
    }

    fn controller_static_ingress_evidence(
        &self,
        input: OfferIngressPlannerInput,
    ) -> OfferIngressEvidence {
        OfferIngressEvidence {
            profile: OfferScopeProfile::ControllerStatic,
            entry: input.entry,
            cursor: input.cursor,
            early_decision: self.controller_early_decision_readiness(&input),
            controller: self.controller_static_readiness(&input),
            passive: OfferPassiveReadiness::NeedsTransport,
            binding_probe: OfferBindingProbeMode::SelectedBinding,
        }
    }

    fn controller_dynamic_ingress_evidence(
        &self,
        input: OfferIngressPlannerInput,
    ) -> OfferIngressEvidence {
        OfferIngressEvidence {
            profile: OfferScopeProfile::ControllerDynamic,
            entry: input.entry,
            cursor: input.cursor,
            early_decision: self.controller_early_decision_readiness(&input),
            controller: self.controller_dynamic_readiness(&input),
            passive: OfferPassiveReadiness::NeedsTransport,
            binding_probe: OfferBindingProbeMode::SelectedBinding,
        }
    }

    fn passive_static_ingress_evidence(
        &self,
        input: OfferIngressPlannerInput,
    ) -> OfferIngressEvidence {
        OfferIngressEvidence {
            profile: OfferScopeProfile::PassiveStatic,
            entry: input.entry,
            cursor: input.cursor,
            early_decision: self.passive_early_decision_readiness(&input),
            controller: OfferControllerReadiness {
                recv_step: OfferControllerRecvStep::NeedsTransportRecv,
                skip: OfferControllerSkipReadiness::NeedsTransport,
            },
            passive: self.passive_static_readiness(&input),
            binding_probe: self.passive_static_binding_probe_mode(&input),
        }
    }

    fn passive_dynamic_ingress_evidence(
        &self,
        input: OfferIngressPlannerInput,
    ) -> OfferIngressEvidence {
        OfferIngressEvidence {
            profile: OfferScopeProfile::PassiveDynamic,
            entry: input.entry,
            cursor: input.cursor,
            early_decision: self.passive_early_decision_readiness(&input),
            controller: OfferControllerReadiness {
                recv_step: OfferControllerRecvStep::NeedsTransportRecv,
                skip: OfferControllerSkipReadiness::NeedsTransport,
            },
            passive: self.passive_dynamic_readiness(&input),
            binding_probe: OfferBindingProbeMode::SelectedBinding,
        }
    }

    fn controller_static_readiness(
        &self,
        input: &OfferIngressPlannerInput,
    ) -> OfferControllerReadiness {
        let profile = OfferScopeProfile::ControllerStatic;
        OfferControllerReadiness {
            recv_step: profile.controller_recv_step(input.entry, self.selected_peer_readiness()),
            skip: profile.controller_skip_readiness(self.controller_skip_evidence(input)),
        }
    }

    fn controller_dynamic_readiness(
        &self,
        input: &OfferIngressPlannerInput,
    ) -> OfferControllerReadiness {
        let profile = OfferScopeProfile::ControllerDynamic;
        OfferControllerReadiness {
            recv_step: profile.controller_recv_step(input.entry, self.selected_peer_readiness()),
            skip: profile.controller_skip_readiness(self.controller_skip_evidence(input)),
        }
    }

    fn controller_skip_evidence(
        &self,
        input: &OfferIngressPlannerInput,
    ) -> OfferControllerSkipEvidence {
        OfferControllerSkipEvidence::new(
            input.cursor,
            self.controller_arm_entry(input),
            self.controller_cursor_arm(input),
            self.controller_materialization_readiness(input),
        )
    }

    fn controller_arm_entry(&self, input: &OfferIngressPlannerInput) -> OfferControllerArmEntry {
        if CursorEndpoint::<ROLE, T, U, C, E, MAX_RV, Mint, B>::scope_has_controller_arm_entry(
            &self.cursor,
            input.selection.scope_id,
        ) {
            OfferControllerArmEntry::Present
        } else {
            OfferControllerArmEntry::Missing
        }
    }

    fn controller_cursor_arm(&self, input: &OfferIngressPlannerInput) -> OfferControllerCursorArm {
        if self
            .controller_arm_at_cursor(input.selection.scope_id)
            .is_some()
        {
            OfferControllerCursorArm::Present
        } else {
            OfferControllerCursorArm::Missing
        }
    }

    fn controller_materialization_readiness(
        &self,
        input: &OfferIngressPlannerInput,
    ) -> OfferMaterializationReadiness {
        match self.selected_arm_for_scope(input.selection.scope_id) {
            Some(arm)
                if self
                    .arm_requires_materialization_ready_evidence(input.selection.scope_id, arm)
                    && !self.scope_has_ready_arm(input.selection.scope_id, arm) =>
            {
                OfferMaterializationReadiness::Pending
            }
            _ => OfferMaterializationReadiness::Ready,
        }
    }

    fn passive_static_readiness(&self, input: &OfferIngressPlannerInput) -> OfferPassiveReadiness {
        OfferScopeProfile::PassiveStatic.passive_readiness(OfferPassiveEvidence::new(
            self.passive_ready_signal(input),
            self.passive_recv_evidence(input),
            OfferPassiveAckEvidence::NotMaterializable,
        ))
    }

    fn passive_dynamic_readiness(&self, input: &OfferIngressPlannerInput) -> OfferPassiveReadiness {
        OfferScopeProfile::PassiveDynamic.passive_readiness(OfferPassiveEvidence::new(
            self.passive_ready_signal(input),
            self.passive_recv_evidence(input),
            self.passive_ack_evidence(input),
        ))
    }

    fn passive_ready_signal(&self, input: &OfferIngressPlannerInput) -> OfferPassiveReadySignal {
        if self.scope_has_ready_arm_evidence(input.selection.scope_id)
            || self
                .peek_scope_frame_hint(input.selection.scope_id)
                .is_some()
        {
            OfferPassiveReadySignal::Present
        } else {
            OfferPassiveReadySignal::Missing
        }
    }

    fn passive_recv_evidence(&self, input: &OfferIngressPlannerInput) -> OfferPassiveRecvEvidence {
        if self.arm_has_recv_with_materialization(
            input.selection.scope_id,
            0,
            input.materialization,
        ) || self.arm_has_recv_with_materialization(
            input.selection.scope_id,
            1,
            input.materialization,
        ) {
            OfferPassiveRecvEvidence::HasRecv
        } else {
            OfferPassiveRecvEvidence::Recvless
        }
    }

    fn passive_ack_evidence(&self, input: &OfferIngressPlannerInput) -> OfferPassiveAckEvidence {
        let Some(token) = input.preview_route_decision else {
            return OfferPassiveAckEvidence::NotMaterializable;
        };
        let arm = token.arm().as_u8();
        if self.scope_has_ready_arm(input.selection.scope_id, arm) {
            return OfferPassiveAckEvidence::Materializable;
        }
        match self.arm_recv_evidence(input.selection.scope_id, arm, input.materialization) {
            OfferArmRecvEvidence::Recvless => OfferPassiveAckEvidence::Materializable,
            OfferArmRecvEvidence::HasRecv => OfferPassiveAckEvidence::NotMaterializable,
        }
    }

    fn controller_early_decision_readiness(
        &self,
        input: &OfferIngressPlannerInput,
    ) -> OfferEarlyDecisionReadiness {
        self.arm_decision_readiness(input, input.preview_route_decision)
    }

    fn passive_early_decision_readiness(
        &self,
        input: &OfferIngressPlannerInput,
    ) -> OfferEarlyDecisionReadiness {
        let decision = match input.preview_route_decision {
            Some(token)
                if self
                    .arm_recv_evidence(
                        input.selection.scope_id,
                        token.arm().as_u8(),
                        input.materialization,
                    )
                    .is_recvless() =>
            {
                Some(token)
            }
            _ => None,
        };
        self.arm_decision_readiness(input, decision)
    }

    fn arm_decision_readiness(
        &self,
        input: &OfferIngressPlannerInput,
        decision: Option<RouteDecisionToken>,
    ) -> OfferEarlyDecisionReadiness {
        OfferEarlyDecisionReadiness::from_arm_evidence(decision.map(|token| {
            self.arm_recv_evidence(
                input.selection.scope_id,
                token.arm().as_u8(),
                input.materialization,
            )
        }))
    }

    fn arm_recv_evidence(
        &self,
        scope_id: crate::global::const_dsl::ScopeId,
        arm: u8,
        materialization: ScopeArmMaterializationMeta,
    ) -> OfferArmRecvEvidence {
        if self.arm_has_recv_with_materialization(scope_id, arm, materialization) {
            OfferArmRecvEvidence::HasRecv
        } else {
            OfferArmRecvEvidence::Recvless
        }
    }

    fn selected_peer_readiness(&self) -> OfferCursorReadiness {
        match self.cursor.try_recv_meta() {
            Some(recv_meta) if recv_meta.peer != ROLE => OfferCursorReadiness::Recv,
            _ => OfferCursorReadiness::NonRecv,
        }
    }

    fn passive_static_binding_probe_mode(
        &self,
        input: &OfferIngressPlannerInput,
    ) -> OfferBindingProbeMode {
        if input.loop_meta.control_scope()
            && !input.loop_meta.arm_has_recv(0)
            && !input.loop_meta.arm_has_recv(1)
        {
            OfferBindingProbeMode::SelectedAndRecvlessLoopBinding
        } else {
            OfferBindingProbeMode::SelectedBinding
        }
    }
}