use core::{fmt, hash::Hash};
use std::{
cmp::Ordering,
ops::{Index, IndexMut},
};
#[repr(u8)]
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
pub enum Priority {
High = 0,
Medium = 1,
Low = 2,
Background = 3,
}
impl Default for Priority {
fn default() -> Self {
Self::DEFAULT
}
}
impl<T> Index<Priority> for [T; Priority::NUM] {
type Output = T;
fn index(&self, index: Priority) -> &Self::Output {
unsafe { self.get_unchecked(index as usize) }
}
}
impl<T> IndexMut<Priority> for [T; Priority::NUM] {
fn index_mut(&mut self, index: Priority) -> &mut Self::Output {
unsafe { self.get_unchecked_mut(index as usize) }
}
}
impl Priority {
pub(crate) const DEFAULT: Self = Self::Medium;
pub(crate) const BITS: u32 = 2;
pub(crate) const MASK: u8 = 0b11;
pub(crate) const MIN: Self = Self::Background;
pub(crate) const MAX: Self = Self::High;
pub(crate) const NUM: usize = 1 + Self::MIN as usize - Self::MAX as usize;
#[cfg(test)]
pub(crate) const RANGE: core::ops::RangeInclusive<u8> = (Priority::MAX as u8..=Priority::MIN as u8);
pub(crate) const ALL: [Priority; Priority::NUM] =
[Priority::High, Priority::Medium, Priority::Low, Priority::Background];
const _CHECK: () = {
assert!(Self::MIN as u8 == 0);
assert!(Self::MAX as u8 == Self::MASK);
};
pub(crate) const unsafe fn from_u8(v: u8) -> Priority {
unsafe { core::mem::transmute(v) }
}
}
impl Priority {
pub const fn as_str(self) -> &'static str {
match self {
Priority::High => "High",
Priority::Medium => "Medium",
Priority::Low => "Low",
Priority::Background => "Background",
}
}
#[cfg(test)]
pub(crate) fn rand() -> Self {
use rand::Rng;
let mut rng = rand::rng();
let value: u8 = rng.random_range(Priority::RANGE);
unsafe { Priority::from_u8(value) }
}
}
impl fmt::Display for Priority {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl PartialOrd for Priority {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Priority {
fn cmp(&self, other: &Self) -> Ordering {
let s = *self as u8;
let o = *other as u8;
(o).cmp(&s)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_u8() {
for priority in Priority::ALL {
let as_u8 = priority as u8;
let restored = unsafe { Priority::from_u8(as_u8) };
assert_eq!(priority, restored);
}
}
#[test]
fn test_priority_ordering() {
assert!(Priority::High > Priority::Medium);
assert!(Priority::Medium > Priority::Low);
assert!(Priority::Low > Priority::Background);
Priority::ALL.iter().fold(None, |prev, p| {
if let Some(prev) = prev {
assert!(prev > *p);
}
Some(*p)
});
}
}