deepslate-protocol 0.1.0

Minecraft protocol primitives for the Deepslate proxy.
Documentation
//! Minecraft protocol version definitions.
//!
//! Only versions 1.21+ are supported. Each entry maps a protocol number
//! to the Minecraft release versions that use it.

/// Minecraft protocol version.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ProtocolVersion {
    /// 1.21, 1.21.1 (protocol 767)
    V1_21,
    /// 1.21.2, 1.21.3 (protocol 768)
    V1_21_2,
    /// 1.21.4 (protocol 769)
    V1_21_4,
    /// 1.21.5 (protocol 770)
    V1_21_5,
    /// 1.21.6 (protocol 771)
    V1_21_6,
    /// 1.21.7, 1.21.8 (protocol 772)
    V1_21_7,
    /// 1.21.9, 1.21.10 (protocol 773)
    V1_21_9,
    /// 1.21.11 (protocol 774)
    V1_21_11,
}

impl ProtocolVersion {
    /// The minimum supported protocol version.
    pub const MINIMUM: Self = Self::V1_21;

    /// The maximum supported protocol version.
    pub const MAXIMUM: Self = Self::V1_21_11;

    /// The minimum protocol number we accept.
    pub const MINIMUM_PROTOCOL: i32 = 767;

    /// The maximum protocol number we accept.
    pub const MAXIMUM_PROTOCOL: i32 = 774;

    /// Create a `ProtocolVersion` from a protocol number.
    ///
    /// Returns `None` if the protocol number is not supported.
    #[must_use]
    pub const fn from_protocol(protocol: i32) -> Option<Self> {
        match protocol {
            767 => Some(Self::V1_21),
            768 => Some(Self::V1_21_2),
            769 => Some(Self::V1_21_4),
            770 => Some(Self::V1_21_5),
            771 => Some(Self::V1_21_6),
            772 => Some(Self::V1_21_7),
            773 => Some(Self::V1_21_9),
            774 => Some(Self::V1_21_11),
            _ => None,
        }
    }

    /// Get the protocol number for this version.
    #[must_use]
    pub const fn protocol(self) -> i32 {
        match self {
            Self::V1_21 => 767,
            Self::V1_21_2 => 768,
            Self::V1_21_4 => 769,
            Self::V1_21_5 => 770,
            Self::V1_21_6 => 771,
            Self::V1_21_7 => 772,
            Self::V1_21_9 => 773,
            Self::V1_21_11 => 774,
        }
    }

    /// Get a human-readable version string.
    #[must_use]
    pub const fn name(self) -> &'static str {
        match self {
            Self::V1_21 => "1.21-1.21.1",
            Self::V1_21_2 => "1.21.2-1.21.3",
            Self::V1_21_4 => "1.21.4",
            Self::V1_21_5 => "1.21.5",
            Self::V1_21_6 => "1.21.6",
            Self::V1_21_7 => "1.21.7-1.21.8",
            Self::V1_21_9 => "1.21.9-1.21.10",
            Self::V1_21_11 => "1.21.11",
        }
    }

    /// Check if this version is at least the given version.
    #[must_use]
    pub const fn at_least(self, other: Self) -> bool {
        self.protocol() >= other.protocol()
    }

    /// The supported version range as a human-readable string.
    pub const SUPPORTED_VERSIONS: &'static str = "1.21-1.21.11";
}

impl std::fmt::Display for ProtocolVersion {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{} ({})", self.name(), self.protocol())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_from_protocol() {
        assert_eq!(
            ProtocolVersion::from_protocol(767),
            Some(ProtocolVersion::V1_21)
        );
        assert_eq!(
            ProtocolVersion::from_protocol(774),
            Some(ProtocolVersion::V1_21_11)
        );
        assert_eq!(ProtocolVersion::from_protocol(0), None);
        assert_eq!(ProtocolVersion::from_protocol(766), None);
        assert_eq!(ProtocolVersion::from_protocol(775), None);
    }

    #[test]
    fn test_roundtrip() {
        for proto in [767, 768, 769, 770, 771, 772, 773, 774] {
            let version = ProtocolVersion::from_protocol(proto).unwrap();
            assert_eq!(version.protocol(), proto);
        }
    }
}