nix_compat/wire/
protocol_version.rs

1/// The latest version that is currently supported by nix-compat.
2static DEFAULT_PROTOCOL_VERSION: ProtocolVersion = ProtocolVersion::from_parts(1, 37);
3
4/// Protocol versions are represented as a u16.
5/// The upper 8 bits are the major version, the lower bits the minor.
6/// This is not aware of any endianness.
7/// If you're receiving over the Nix worker protocol, you need to read a
8/// little-endian encoded u64, and then use the try_from() impl to convert to this
9/// type.
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11pub struct ProtocolVersion(u16);
12
13impl ProtocolVersion {
14    pub const fn from_parts(major: u8, minor: u8) -> Self {
15        Self(((major as u16) << 8) | minor as u16)
16    }
17
18    pub fn major(&self) -> u8 {
19        ((self.0 & 0xff00) >> 8) as u8
20    }
21
22    pub fn minor(&self) -> u8 {
23        (self.0 & 0x00ff) as u8
24    }
25}
26
27impl Default for ProtocolVersion {
28    fn default() -> Self {
29        DEFAULT_PROTOCOL_VERSION
30    }
31}
32
33impl PartialOrd for ProtocolVersion {
34    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
35        Some(self.cmp(other))
36    }
37}
38
39impl Ord for ProtocolVersion {
40    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
41        match self.major().cmp(&other.major()) {
42            std::cmp::Ordering::Less => std::cmp::Ordering::Less,
43            std::cmp::Ordering::Greater => std::cmp::Ordering::Greater,
44            std::cmp::Ordering::Equal => {
45                // same major, compare minor
46                self.minor().cmp(&other.minor())
47            }
48        }
49    }
50}
51
52impl From<u16> for ProtocolVersion {
53    fn from(value: u16) -> Self {
54        Self::from_parts(((value & 0xff00) >> 8) as u8, (value & 0x00ff) as u8)
55    }
56}
57
58#[cfg(any(test, feature = "test"))]
59impl From<(u8, u8)> for ProtocolVersion {
60    fn from((major, minor): (u8, u8)) -> Self {
61        Self::from_parts(major, minor)
62    }
63}
64
65impl TryFrom<u64> for ProtocolVersion {
66    type Error = &'static str;
67
68    fn try_from(value: u64) -> Result<Self, Self::Error> {
69        if value & !0xffff != 0 {
70            return Err("only two least significant bits might be populated");
71        }
72
73        Ok((value as u16).into())
74    }
75}
76
77impl From<ProtocolVersion> for u16 {
78    fn from(value: ProtocolVersion) -> Self {
79        value.0
80    }
81}
82
83impl From<ProtocolVersion> for u64 {
84    fn from(value: ProtocolVersion) -> Self {
85        value.0 as u64
86    }
87}
88
89impl std::fmt::Display for ProtocolVersion {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        write!(f, "{}.{}", self.major(), self.minor())
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::ProtocolVersion;
98
99    #[test]
100    fn from_parts() {
101        let version = ProtocolVersion::from_parts(1, 37);
102        assert_eq!(version.major(), 1, "correct major");
103        assert_eq!(version.minor(), 37, "correct minor");
104        assert_eq!("1.37", &version.to_string(), "to_string");
105
106        assert_eq!(0x0125, Into::<u16>::into(version));
107        assert_eq!(0x0125, Into::<u64>::into(version));
108    }
109
110    #[test]
111    fn from_u16() {
112        let version = ProtocolVersion::from(0x0125_u16);
113        assert_eq!("1.37", &version.to_string());
114    }
115
116    #[test]
117    fn from_u64() {
118        let version = ProtocolVersion::try_from(0x0125_u64).expect("must succeed");
119        assert_eq!("1.37", &version.to_string());
120    }
121
122    /// This contains data in higher bits, which should fail.
123    #[test]
124    fn from_u64_fail() {
125        ProtocolVersion::try_from(0xaa0125_u64).expect_err("must fail");
126    }
127
128    #[test]
129    fn ord() {
130        let v0_37 = ProtocolVersion::from_parts(0, 37);
131        let v1_37 = ProtocolVersion::from_parts(1, 37);
132        let v1_40 = ProtocolVersion::from_parts(1, 40);
133
134        assert!(v0_37 < v1_37);
135        assert!(v1_37 > v0_37);
136        assert!(v1_37 < v1_40);
137        assert!(v1_40 > v1_37);
138        assert!(v1_40 <= v1_40);
139    }
140}