hibana 0.9.6

Choreography-derived runtime enforcement kernel for no_std Rust multiparty protocols
Documentation
//! Authority-path helpers for resolver/ack/poll decisions.

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) struct Arm(u8);

impl Arm {
    pub(super) const LEFT: Self = Self(0);
    pub(super) const RIGHT: Self = Self(1);

    #[inline]
    pub(super) const fn decode_raw(value: u8) -> Option<Self> {
        match value {
            0 => Some(Self::LEFT),
            1 => Some(Self::RIGHT),
            2..=u8::MAX => None,
        }
    }

    #[inline]
    pub(super) const fn from_raw(value: u8) -> Self {
        match Self::decode_raw(value) {
            Some(arm) => arm,
            None => crate::invariant(),
        }
    }

    #[inline]
    const fn decode_single_ready_mask(mask: u8) -> Option<Option<Self>> {
        match mask {
            0 => Some(None),
            1 => Some(Some(Self::LEFT)),
            2 => Some(Some(Self::RIGHT)),
            3..=u8::MAX => None,
        }
    }

    #[inline]
    pub(super) const fn from_single_ready_mask(mask: u8) -> Option<Self> {
        match Self::decode_single_ready_mask(mask) {
            Some(arm) => arm,
            None => crate::invariant(),
        }
    }

    #[inline]
    pub(super) const fn as_u8(self) -> u8 {
        self.0
    }
}

#[cfg(kani)]
mod kani;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) enum RouteArmToken {
    Ack(Arm),
    Resolver(Arm),
    Poll(Arm),
}

impl RouteArmToken {
    #[inline]
    pub(super) const fn from_ack(arm: Arm) -> Self {
        Self::Ack(arm)
    }

    #[inline]
    pub(super) const fn from_resolver(arm: Arm) -> Self {
        Self::Resolver(arm)
    }

    #[inline]
    pub(super) const fn from_poll(arm: Arm) -> Self {
        Self::Poll(arm)
    }

    #[inline]
    pub(super) const fn arm(self) -> Arm {
        match self {
            Self::Ack(arm) | Self::Resolver(arm) | Self::Poll(arm) => arm,
        }
    }

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

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

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

    #[inline]
    pub(super) const fn as_tap_seq(self) -> u8 {
        match self {
            Self::Ack(_) => 1,
            Self::Resolver(_) => 2,
            Self::Poll(_) => 3,
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) enum RouteResolveStep {
    Resolved(Arm),
    Reject(u16),
}

#[cfg(test)]
mod tests {
    use super::{Arm, RouteArmToken};

    #[test]
    fn route_arm_token_carries_arm_and_authority_together() {
        let left = Arm::LEFT;
        assert_eq!(RouteArmToken::from_ack(left).arm(), left);
        assert_eq!(RouteArmToken::from_ack(left).as_tap_seq(), 1);
        assert_eq!(RouteArmToken::from_resolver(left).as_tap_seq(), 2);
        assert_eq!(RouteArmToken::from_poll(left).as_tap_seq(), 3);
        assert!(RouteArmToken::from_ack(left).is_ack());
        assert!(RouteArmToken::from_resolver(left).is_resolver());
        assert!(RouteArmToken::from_poll(left).is_poll());
    }

    #[test]
    fn raw_route_arm_and_ready_mask_decode_exact_binary_authority() {
        for raw in 0..=u8::MAX {
            assert_eq!(Arm::decode_raw(raw).is_some(), raw <= 1);
            let expected = match raw {
                0 => Some(None),
                1 => Some(Some(Arm::LEFT)),
                2 => Some(Some(Arm::RIGHT)),
                3..=u8::MAX => None,
            };
            assert_eq!(Arm::decode_single_ready_mask(raw), expected);
        }
        assert_eq!(Arm::from_single_ready_mask(0), None);
        assert_eq!(Arm::from_single_ready_mask(1), Some(Arm::LEFT));
        assert_eq!(Arm::from_single_ready_mask(2), Some(Arm::RIGHT));
    }

    #[test]
    #[should_panic]
    fn conflicting_ready_mask_fails_closed() {
        let _ = Arm::from_single_ready_mask(3);
    }

    #[test]
    #[should_panic]
    fn invalid_raw_route_arm_fails_closed() {
        let _ = Arm::from_raw(2);
    }

    #[test]
    #[should_panic]
    fn invalid_single_bit_ready_mask_fails_closed() {
        let _ = Arm::from_single_ready_mask(1 << 2);
    }

    #[test]
    #[should_panic]
    fn invalid_mixed_ready_mask_fails_closed() {
        let _ = Arm::from_single_ready_mask((1 << 2) | 1);
    }
}