1#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10#[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern))]
11#[repr(transparent)]
12pub struct Version16Dot16(u32);
13
14#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24#[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern))]
25#[repr(C)]
26pub struct MajorMinor {
27 pub major: u16,
29 pub minor: u16,
31}
32
33pub trait Compatible<Rhs = Self>: Sized {
35 fn compatible(&self, other: Rhs) -> bool;
40}
41
42impl Version16Dot16 {
43 pub const VERSION_0_5: Version16Dot16 = Version16Dot16::new(0, 5);
45 pub const VERSION_1_0: Version16Dot16 = Version16Dot16::new(1, 0);
47 pub const VERSION_1_1: Version16Dot16 = Version16Dot16::new(1, 1);
49 pub const VERSION_2_0: Version16Dot16 = Version16Dot16::new(2, 0);
51 pub const VERSION_2_5: Version16Dot16 = Version16Dot16::new(2, 5);
53 pub const VERSION_3_0: Version16Dot16 = Version16Dot16::new(3, 0);
55
56 pub const fn new(major: u16, minor: u16) -> Self {
61 let minor = if minor > 9 { 9 } else { minor };
63 let version = ((major as u32) << 16) | ((minor as u32) << 12);
64 Version16Dot16(version)
65 }
66
67 pub const fn to_major_minor(self) -> (u16, u16) {
69 let major = (self.0 >> 16) as u16;
70 let minor = ((self.0 & 0xFFFF) >> 12) as u16;
71 (major, minor)
72 }
73
74 #[inline]
76 pub const fn to_be_bytes(self) -> [u8; 4] {
77 self.0.to_be_bytes()
78 }
79}
80
81crate::newtype_scalar!(Version16Dot16, [u8; 4]);
82
83impl MajorMinor {
84 pub const VERSION_1_0: MajorMinor = MajorMinor::new(1, 0);
86 pub const VERSION_1_1: MajorMinor = MajorMinor::new(1, 1);
88 pub const VERSION_1_2: MajorMinor = MajorMinor::new(1, 2);
90 pub const VERSION_1_3: MajorMinor = MajorMinor::new(1, 3);
92 pub const VERSION_2_0: MajorMinor = MajorMinor::new(2, 0);
94
95 #[inline]
97 pub const fn new(major: u16, minor: u16) -> Self {
98 MajorMinor { major, minor }
99 }
100
101 #[inline]
103 pub const fn to_be_bytes(self) -> [u8; 4] {
104 let [a, b] = self.major.to_be_bytes();
105 let [c, d] = self.minor.to_be_bytes();
106 [a, b, c, d]
107 }
108}
109
110impl crate::Scalar for MajorMinor {
111 type Raw = [u8; 4];
112
113 fn from_raw(raw: Self::Raw) -> Self {
114 let major = u16::from_be_bytes([raw[0], raw[1]]);
115 let minor = u16::from_be_bytes([raw[2], raw[3]]);
116 Self { major, minor }
117 }
118
119 fn to_raw(self) -> Self::Raw {
120 self.to_be_bytes()
121 }
122}
123
124impl Compatible for Version16Dot16 {
125 #[inline]
126 fn compatible(&self, other: Self) -> bool {
127 let (self_major, self_minor) = self.to_major_minor();
128 let (other_major, other_minor) = other.to_major_minor();
129 self_major == other_major && self_minor >= other_minor
130 }
131}
132
133impl Compatible<(u16, u16)> for Version16Dot16 {
134 fn compatible(&self, other: (u16, u16)) -> bool {
135 self.compatible(Version16Dot16::new(other.0, other.1))
136 }
137}
138
139impl Compatible for MajorMinor {
140 #[inline]
141 fn compatible(&self, other: Self) -> bool {
142 self.major == other.major && self.minor >= other.minor
143 }
144}
145
146impl Compatible<(u16, u16)> for MajorMinor {
147 fn compatible(&self, other: (u16, u16)) -> bool {
148 self.compatible(MajorMinor::new(other.0, other.1))
149 }
150}
151
152impl Compatible for u16 {
153 #[inline]
154 fn compatible(&self, other: Self) -> bool {
155 *self >= other
156 }
157}
158
159impl std::fmt::Debug for Version16Dot16 {
160 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
161 write!(f, "Version16Dot16({:08x})", self.0)
162 }
163}
164
165impl std::fmt::Display for Version16Dot16 {
166 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
167 let (major, minor) = self.to_major_minor();
168 write!(f, "{major}.{minor}")
169 }
170}
171
172impl std::fmt::Display for MajorMinor {
173 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
174 let MajorMinor { major, minor } = self;
175 write!(f, "{major}.{minor}")
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182 #[test]
183 fn version_smoke_test() {
184 assert_eq!(Version16Dot16(0x00005000).to_major_minor(), (0, 5));
185 assert_eq!(Version16Dot16(0x00011000).to_major_minor(), (1, 1));
186 assert_eq!(Version16Dot16::new(0, 5).0, 0x00005000);
187 assert_eq!(Version16Dot16::new(1, 1).0, 0x00011000);
188 }
189
190 #[test]
191 fn minor_version_clamped_test() {
192 for minor in 0..=9 {
193 assert_eq!(Version16Dot16::new(1, minor).to_major_minor(), (1, minor));
194 }
195 for minor in [10, u16::MAX] {
197 assert_eq!(Version16Dot16::new(1, minor).to_major_minor(), (1, 9));
198 }
199 }
200}