#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern))]
#[repr(transparent)]
pub struct Version16Dot16(u32);
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::AnyBitPattern))]
#[repr(C)]
pub struct MajorMinor {
pub major: u16,
pub minor: u16,
}
pub trait Compatible<Rhs = Self>: Sized {
fn compatible(&self, other: Rhs) -> bool;
}
impl Version16Dot16 {
pub const VERSION_0_5: Version16Dot16 = Version16Dot16::new(0, 5);
pub const VERSION_1_0: Version16Dot16 = Version16Dot16::new(1, 0);
pub const VERSION_1_1: Version16Dot16 = Version16Dot16::new(1, 1);
pub const VERSION_2_0: Version16Dot16 = Version16Dot16::new(2, 0);
pub const VERSION_2_5: Version16Dot16 = Version16Dot16::new(2, 5);
pub const VERSION_3_0: Version16Dot16 = Version16Dot16::new(3, 0);
pub const fn new(major: u16, minor: u16) -> Self {
assert!(minor < 10, "minor version must be in the range [0, 9)");
let version = (major as u32) << 16 | (minor as u32) << 12;
Version16Dot16(version)
}
pub const fn to_major_minor(self) -> (u16, u16) {
let major = (self.0 >> 16) as u16;
let minor = ((self.0 & 0xFFFF) >> 12) as u16;
(major, minor)
}
#[inline]
pub const fn to_be_bytes(self) -> [u8; 4] {
self.0.to_be_bytes()
}
}
crate::newtype_scalar!(Version16Dot16, [u8; 4]);
impl MajorMinor {
pub const VERSION_1_0: MajorMinor = MajorMinor::new(1, 0);
pub const VERSION_1_1: MajorMinor = MajorMinor::new(1, 1);
pub const VERSION_1_2: MajorMinor = MajorMinor::new(1, 2);
pub const VERSION_1_3: MajorMinor = MajorMinor::new(1, 3);
pub const VERSION_2_0: MajorMinor = MajorMinor::new(2, 0);
#[inline]
pub const fn new(major: u16, minor: u16) -> Self {
MajorMinor { major, minor }
}
#[inline]
pub const fn to_be_bytes(self) -> [u8; 4] {
let [a, b] = self.major.to_be_bytes();
let [c, d] = self.minor.to_be_bytes();
[a, b, c, d]
}
}
impl crate::Scalar for MajorMinor {
type Raw = [u8; 4];
fn from_raw(raw: Self::Raw) -> Self {
let major = u16::from_be_bytes([raw[0], raw[1]]);
let minor = u16::from_be_bytes([raw[2], raw[3]]);
Self { major, minor }
}
fn to_raw(self) -> Self::Raw {
self.to_be_bytes()
}
}
impl Compatible for Version16Dot16 {
#[inline]
fn compatible(&self, other: Self) -> bool {
let (self_major, self_minor) = self.to_major_minor();
let (other_major, other_minor) = other.to_major_minor();
self_major == other_major && self_minor >= other_minor
}
}
impl Compatible<(u16, u16)> for Version16Dot16 {
fn compatible(&self, other: (u16, u16)) -> bool {
self.compatible(Version16Dot16::new(other.0, other.1))
}
}
impl Compatible for MajorMinor {
#[inline]
fn compatible(&self, other: Self) -> bool {
self.major == other.major && self.minor >= other.minor
}
}
impl Compatible<(u16, u16)> for MajorMinor {
fn compatible(&self, other: (u16, u16)) -> bool {
self.compatible(MajorMinor::new(other.0, other.1))
}
}
impl Compatible for u16 {
#[inline]
fn compatible(&self, other: Self) -> bool {
*self >= other
}
}
impl std::fmt::Debug for Version16Dot16 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Version16Dot16({:08x})", self.0)
}
}
impl std::fmt::Display for Version16Dot16 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let (major, minor) = self.to_major_minor();
write!(f, "{major}.{minor}")
}
}
impl std::fmt::Display for MajorMinor {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let MajorMinor { major, minor } = self;
write!(f, "{major}.{minor}")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn version_smoke_test() {
assert_eq!(Version16Dot16(0x00005000).to_major_minor(), (0, 5));
assert_eq!(Version16Dot16(0x00011000).to_major_minor(), (1, 1));
assert_eq!(Version16Dot16::new(0, 5).0, 0x00005000);
assert_eq!(Version16Dot16::new(1, 1).0, 0x00011000);
}
}