use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MumbleVersion {
pub major: u16,
pub minor: u16,
pub patch: u16,
}
impl MumbleVersion {
pub const CLIENT: Self = Self::new(1, 5, 0);
pub const LISTENING: Self = Self::new(1, 4, 0);
pub const PLUGIN_DATA: Self = Self::new(1, 4, 0);
pub const LISTENER_VOLUME: Self = Self::new(1, 5, 0);
pub const fn new(major: u16, minor: u16, patch: u16) -> Self {
Self {
major,
minor,
patch,
}
}
pub fn from_v1(value: u32) -> Self {
Self::new(
(value >> 16) as u16,
((value >> 8) & 0xff) as u16,
(value & 0xff) as u16,
)
}
pub fn from_v2(value: u64) -> Self {
Self::new(
(value >> 48) as u16,
(value >> 32) as u16,
(value >> 16) as u16,
)
}
pub fn to_v1(self) -> u32 {
(u32::from(self.major) << 16)
| (u32::from(self.minor.min(0xff)) << 8)
| u32::from(self.patch.min(0xff))
}
pub fn to_v2(self) -> u64 {
(u64::from(self.major) << 48)
| (u64::from(self.minor) << 32)
| (u64::from(self.patch) << 16)
}
pub fn from_wire(v1: Option<u32>, v2: Option<u64>) -> Option<Self> {
match (v2, v1) {
(Some(v2), _) if v2 != 0 => Some(Self::from_v2(v2)),
(_, Some(v1)) => Some(Self::from_v1(v1)),
_ => None,
}
}
pub fn supports_listening(self) -> bool {
self >= Self::LISTENING
}
pub fn supports_listener_volume(self) -> bool {
self >= Self::LISTENER_VOLUME
}
}
impl fmt::Display for MumbleVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encodings_round_trip() {
let v = MumbleVersion::new(1, 5, 735);
assert_eq!(MumbleVersion::from_v2(v.to_v2()), v);
assert_eq!(MumbleVersion::CLIENT.to_v1(), 0x0001_0500);
assert_eq!(MumbleVersion::CLIENT.to_v2(), 0x0001_0005_0000_0000);
assert_eq!(
MumbleVersion::from_v1(0x0001_0402),
MumbleVersion::new(1, 4, 2)
);
assert_eq!(v.to_v1(), 0x0001_05ff);
}
#[test]
fn wire_prefers_v2() {
let v2 = MumbleVersion::new(1, 5, 735).to_v2();
assert_eq!(
MumbleVersion::from_wire(Some(0x0001_05ff), Some(v2)),
Some(MumbleVersion::new(1, 5, 735))
);
assert_eq!(
MumbleVersion::from_wire(Some(0x0001_0300), None),
Some(MumbleVersion::new(1, 3, 0))
);
assert_eq!(MumbleVersion::from_wire(None, None), None);
}
#[test]
fn capabilities() {
assert!(!MumbleVersion::new(1, 3, 4).supports_listening());
assert!(MumbleVersion::new(1, 4, 230).supports_listening());
assert!(!MumbleVersion::new(1, 4, 230).supports_listener_volume());
assert!(MumbleVersion::new(1, 5, 735).supports_listener_volume());
}
}