hibana 0.9.3

Session-typed choreographic programming for no_std Rust protocols, inspired by affine MPST
Documentation
use super::super::{
    CursorEndpoint, OfferScopeProfile, OfferScopeSelection, RouteArmToken,
    ScopeArmMaterializationMeta, Transport,
    profile::{
        OfferArmRecvEvidence, OfferControllerCursorArm, OfferControllerLocalEvidence,
        OfferControllerLocalReadiness, OfferCursorReadiness, OfferEarlyDecisionReadiness,
        OfferEntryPosition, OfferMaterializationReadiness, OfferPassiveAckEvidence,
        OfferPassiveEvidence, OfferPassiveReadiness, OfferPassiveReadySignal,
        OfferPassiveRecvEvidence,
    },
};
use super::evidence::OfferIngressEvidence;

struct OfferIngressPlannerInput {
    selection: OfferScopeSelection,
    entry: OfferEntryPosition,
    materialization: ScopeArmMaterializationMeta,
    preview_route_arm_selection: Option<RouteArmToken>,
    cursor: OfferCursorReadiness,
}

impl<'r, const ROLE: u8, T> CursorEndpoint<'r, ROLE, T>
where
    T: Transport + '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 input = OfferIngressPlannerInput {
            selection,
            entry,
            materialization: self.selection_materialization_meta(selection),
            preview_route_arm_selection: self
                .preview_scope_ack_token_non_consuming(scope_id, offer_lanes),
            cursor: if self.cursor.is_recv() {
                OfferCursorReadiness::Recv
            } else {
                OfferCursorReadiness::NonRecv
            },
        };

        match profile {
            OfferScopeProfile::ControllerIntrinsic => {
                self.controller_intrinsic_ingress_evidence(input)
            }
            OfferScopeProfile::ControllerDynamic => self.controller_dynamic_ingress_evidence(input),
            OfferScopeProfile::PassiveIntrinsic => self.passive_intrinsic_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 is_controller = self.cursor.is_route_controller(scope_id);
        let is_dynamic = self
            .cursor
            .route_scope_controller_resolver(scope_id)
            .is_some_and(|(resolver, _)| resolver.is_dynamic());
        match (is_controller, is_dynamic) {
            (true, true) => OfferScopeProfile::ControllerDynamic,
            (true, false) => OfferScopeProfile::ControllerIntrinsic,
            (false, true) => OfferScopeProfile::PassiveDynamic,
            (false, false) => OfferScopeProfile::PassiveIntrinsic,
        }
    }

    fn controller_intrinsic_ingress_evidence(
        &self,
        input: OfferIngressPlannerInput,
    ) -> OfferIngressEvidence {
        OfferIngressEvidence {
            profile: OfferScopeProfile::ControllerIntrinsic,
            entry: input.entry,
            cursor: input.cursor,
            early_decision: self.controller_early_decision_readiness(&input),
            controller: self.controller_intrinsic_readiness(&input),
            passive: OfferPassiveReadiness::NeedsTransport,
        }
    }

    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,
        }
    }

    fn passive_intrinsic_ingress_evidence(
        &self,
        input: OfferIngressPlannerInput,
    ) -> OfferIngressEvidence {
        OfferIngressEvidence {
            profile: OfferScopeProfile::PassiveIntrinsic,
            entry: input.entry,
            cursor: input.cursor,
            early_decision: self.passive_early_decision_readiness(&input),
            controller: OfferControllerLocalReadiness::NeedsTransport,
            passive: self.passive_intrinsic_readiness(&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: OfferControllerLocalReadiness::NeedsTransport,
            passive: self.passive_dynamic_readiness(&input),
        }
    }

    fn controller_intrinsic_readiness(
        &self,
        input: &OfferIngressPlannerInput,
    ) -> OfferControllerLocalReadiness {
        let profile = OfferScopeProfile::ControllerIntrinsic;
        profile.controller_local_readiness(self.controller_local_evidence(input))
    }

    fn controller_dynamic_readiness(
        &self,
        input: &OfferIngressPlannerInput,
    ) -> OfferControllerLocalReadiness {
        let profile = OfferScopeProfile::ControllerDynamic;
        profile.controller_local_readiness(self.controller_local_evidence(input))
    }

    fn controller_local_evidence(
        &self,
        input: &OfferIngressPlannerInput,
    ) -> OfferControllerLocalEvidence {
        OfferControllerLocalEvidence::new(
            input.cursor,
            self.controller_cursor_arm(input),
            self.controller_materialization_readiness(input),
        )
    }

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

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

    fn passive_intrinsic_readiness(
        &self,
        input: &OfferIngressPlannerInput,
    ) -> OfferPassiveReadiness {
        OfferScopeProfile::PassiveIntrinsic.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::Observed
        } else {
            OfferPassiveReadySignal::Absent
        }
    }

    fn passive_recv_evidence(&self, input: &OfferIngressPlannerInput) -> OfferPassiveRecvEvidence {
        if matches!(
            self.arm_recv_evidence(input, 0),
            OfferArmRecvEvidence::HasRecv
        ) || matches!(
            self.arm_recv_evidence(input, 1),
            OfferArmRecvEvidence::HasRecv
        ) {
            OfferPassiveRecvEvidence::HasRecv
        } else {
            OfferPassiveRecvEvidence::Recvless
        }
    }

    fn passive_ack_evidence(&self, input: &OfferIngressPlannerInput) -> OfferPassiveAckEvidence {
        let Some(token) = input.preview_route_arm_selection 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, arm) {
            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_arm_selection)
    }

    fn passive_early_decision_readiness(
        &self,
        input: &OfferIngressPlannerInput,
    ) -> OfferEarlyDecisionReadiness {
        let Some(token) = input.preview_route_arm_selection else {
            return self.arm_decision_readiness(input, None);
        };
        let decision = match self.arm_recv_evidence(input, token.arm().as_u8()) {
            OfferArmRecvEvidence::Recvless => Some(token),
            OfferArmRecvEvidence::HasRecv => None,
        };
        self.arm_decision_readiness(input, decision)
    }

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

    fn arm_recv_evidence(&self, input: &OfferIngressPlannerInput, arm: u8) -> OfferArmRecvEvidence {
        let scope_id = input.selection.scope_id;
        if self.arm_has_recv_with_materialization(scope_id, arm, input.materialization)
            || self
                .compute_passive_arm_recv_meta(scope_id, arm, input.selection.offer_lane)
                .is_recv_step()
        {
            OfferArmRecvEvidence::HasRecv
        } else {
            OfferArmRecvEvidence::Recvless
        }
    }
}