Skip to main content

kojacoord_protocol/
negotiation.rs

1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2pub enum ProtocolVersion {
3    V1_6_4,
4
5    V1_7_10,
6
7    V1_8,
8
9    V1_12_2,
10
11    V1_16_5,
12
13    V1_19_4,
14
15    V1_20_4,
16
17    V1_21,
18
19    Unknown(u32),
20}
21
22impl ProtocolVersion {
23    pub fn from_id(id: u32) -> Self {
24        match id {
25            5 => ProtocolVersion::V1_7_10,
26            47 => ProtocolVersion::V1_8,
27            78 => ProtocolVersion::V1_6_4,
28            340 => ProtocolVersion::V1_12_2,
29            754 => ProtocolVersion::V1_16_5,
30            762 => ProtocolVersion::V1_19_4,
31            765 => ProtocolVersion::V1_20_4,
32            767 => ProtocolVersion::V1_21,
33            x => ProtocolVersion::Unknown(x),
34        }
35    }
36
37    pub fn id(&self) -> u32 {
38        match self {
39            ProtocolVersion::V1_6_4 => 78,
40            ProtocolVersion::V1_7_10 => 5,
41            ProtocolVersion::V1_8 => 47,
42            ProtocolVersion::V1_12_2 => 340,
43            ProtocolVersion::V1_16_5 => 754,
44            ProtocolVersion::V1_19_4 => 762,
45            ProtocolVersion::V1_20_4 => 765,
46            ProtocolVersion::V1_21 => 767,
47            ProtocolVersion::Unknown(x) => *x,
48        }
49    }
50
51    pub fn is_supported(&self) -> bool {
52        !matches!(self, ProtocolVersion::Unknown(_))
53    }
54}
55
56pub struct VersionRegistry;
57
58impl VersionRegistry {
59    const SUPPORTED: &'static [u32] = &[5, 47, 78, 340, 754, 762, 765, 767];
60
61    pub fn nearest(protocol_id: u32) -> ProtocolVersion {
62        let exact = ProtocolVersion::from_id(protocol_id);
63        if exact.is_supported() {
64            return exact;
65        }
66
67        let best = Self::SUPPORTED
68            .iter()
69            .copied()
70            .min_by_key(|&s| (s as i64 - protocol_id as i64).unsigned_abs())
71            .unwrap_or(767);
72
73        ProtocolVersion::from_id(best)
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn known_versions_roundtrip() {
83        for &id in VersionRegistry::SUPPORTED {
84            let v = ProtocolVersion::from_id(id);
85            assert!(v.is_supported());
86            assert_eq!(v.id(), id);
87        }
88    }
89
90    #[test]
91    fn v1_7_10_recognized() {
92        assert_eq!(ProtocolVersion::from_id(5), ProtocolVersion::V1_7_10);
93    }
94
95    #[test]
96    fn v1_6_4_recognized() {
97        assert_eq!(ProtocolVersion::from_id(78), ProtocolVersion::V1_6_4);
98    }
99
100    #[test]
101    fn nearest_exact() {
102        assert_eq!(VersionRegistry::nearest(47), ProtocolVersion::V1_8);
103        assert_eq!(VersionRegistry::nearest(5), ProtocolVersion::V1_7_10);
104    }
105
106    #[test]
107    fn nearest_between_versions() {
108        let v = VersionRegistry::nearest(400);
109        assert_eq!(v, ProtocolVersion::V1_12_2);
110    }
111}