use core::fmt;
use super::{CongestionControl, priority::Priority};
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct QoS(u8);
impl QoS {
pub const DEFAULT: Self = Self(Priority::DEFAULT as u8);
const DROPPABLE: u8 = 1 << Priority::BITS;
const EXPRESS: u8 = 1 << (Priority::BITS + 1);
pub const fn default() -> Self {
Self::DEFAULT
}
pub const fn priority(&self) -> Priority {
unsafe { Priority::from_u8(self.0 & Priority::MASK) }
}
pub const fn with_priority(mut self, priority: Priority) -> Self {
self.0 &= !Priority::MASK;
self.0 |= priority as u8;
self
}
pub const fn express(&self) -> bool {
self.0 & Self::EXPRESS != 0
}
pub const fn with_express(mut self, express: bool) -> Self {
if express {
self.0 |= Self::EXPRESS;
} else {
self.0 &= !Self::EXPRESS;
}
self
}
pub const fn congestion_control(&self) -> CongestionControl {
unsafe { CongestionControl::from_bool(self.0 & Self::DROPPABLE == Self::DROPPABLE) }
}
pub const fn with_congestion_control(mut self, congestion_control: CongestionControl) -> Self {
match congestion_control {
CongestionControl::Block => self.0 &= !Self::DROPPABLE,
CongestionControl::Drop => self.0 |= Self::DROPPABLE,
}
self
}
pub(crate) const fn as_u8(&self) -> u8 {
self.0
}
pub(crate) const unsafe fn from_u8(v: u8) -> Self {
Self(v)
}
#[cfg(test)]
pub(crate) fn rand() -> Self {
QoS::default()
.with_priority(Priority::rand())
.with_congestion_control(CongestionControl::rand())
.with_express(rand::random_bool(0.5))
}
}
impl fmt::Debug for QoS {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("QoS")
.field("priority", &self.priority())
.field("congestion_control", &self.congestion_control())
.field("express", &self.express())
.finish()
}
}
impl Default for QoS {
fn default() -> Self {
Self::DEFAULT
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_qos_new() {
let qos = QoS::default();
assert_eq!(qos, QoS::DEFAULT);
assert_eq!(qos, QoS::default());
assert_eq!(qos.priority(), Priority::DEFAULT);
assert_eq!(qos.congestion_control(), CongestionControl::DEFAULT);
assert!(!qos.express());
}
#[test]
fn test_priorities() {
for priority in Priority::ALL {
let qos = QoS::default().with_priority(priority);
assert_eq!(qos.priority(), priority);
}
}
#[test]
fn test_express_flag() {
let qos = QoS::default().with_express(true);
assert!(qos.express());
let qos = QoS::default().with_express(false);
assert!(!qos.express());
}
#[test]
fn test_congestion_control() {
let qos = QoS::default().with_congestion_control(CongestionControl::Drop);
assert_eq!(qos.congestion_control(), CongestionControl::Drop);
let qos = QoS::default().with_congestion_control(CongestionControl::Block);
assert_eq!(qos.congestion_control(), CongestionControl::Block);
}
#[test]
fn test_combined_settings() {
let qos = QoS::default()
.with_priority(Priority::High)
.with_express(true)
.with_congestion_control(CongestionControl::Drop);
assert_eq!(qos.priority(), Priority::High);
assert!(qos.express());
assert_eq!(qos.congestion_control(), CongestionControl::Drop);
}
#[test]
fn test_as_u8_and_from_u8() {
let qos = QoS::default()
.with_priority(Priority::Low)
.with_express(true)
.with_congestion_control(CongestionControl::Drop);
let byte = qos.as_u8();
let restored = unsafe { QoS::from_u8(byte) };
assert_eq!(qos, restored);
}
}