use std::cmp::Ordering;
use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PriorityClass {
RealTime,
Interactive,
Normal,
Background,
Idle,
}
impl PriorityClass {
pub fn weight(&self) -> i32 {
match self {
Self::RealTime => 1000,
Self::Interactive => 500,
Self::Normal => 100,
Self::Background => 10,
Self::Idle => 1,
}
}
pub fn all_descending() -> &'static [PriorityClass] {
&[
PriorityClass::RealTime,
PriorityClass::Interactive,
PriorityClass::Normal,
PriorityClass::Background,
PriorityClass::Idle,
]
}
}
impl fmt::Display for PriorityClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::RealTime => write!(f, "RealTime"),
Self::Interactive => write!(f, "Interactive"),
Self::Normal => write!(f, "Normal"),
Self::Background => write!(f, "Background"),
Self::Idle => write!(f, "Idle"),
}
}
}
impl Default for PriorityClass {
fn default() -> Self {
Self::Normal
}
}
impl Ord for PriorityClass {
fn cmp(&self, other: &Self) -> Ordering {
self.weight().cmp(&other.weight())
}
}
impl PartialOrd for PriorityClass {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PriorityNodeId(pub u64);
impl fmt::Display for PriorityNodeId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Node({})", self.0)
}
}
#[derive(Debug, Clone)]
pub struct NodePriorityEntry {
pub node_id: PriorityNodeId,
pub class: PriorityClass,
pub boost: i32,
pub effective: i32,
pub label: String,
}
impl NodePriorityEntry {
pub fn new(node_id: PriorityNodeId, class: PriorityClass, label: &str) -> Self {
let effective = class.weight();
Self {
node_id,
class,
boost: 0,
effective,
label: label.to_string(),
}
}
pub fn with_boost(mut self, boost: i32) -> Self {
self.boost = boost.clamp(-500, 500);
self.effective = self.class.weight() + self.boost;
self
}
pub fn recalculate(&mut self) {
self.effective = self.class.weight() + self.boost;
}
}
impl fmt::Display for NodePriorityEntry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} [{}] priority={} ({}{})",
self.label,
self.node_id,
self.effective,
self.class,
if self.boost != 0 {
format!("{:+}", self.boost)
} else {
String::new()
}
)
}
}
pub struct PriorityManager {
entries: HashMap<PriorityNodeId, NodePriorityEntry>,
default_class: PriorityClass,
}
impl PriorityManager {
pub fn new() -> Self {
Self {
entries: HashMap::new(),
default_class: PriorityClass::Normal,
}
}
pub fn set_default_class(&mut self, class: PriorityClass) {
self.default_class = class;
}
pub fn default_class(&self) -> PriorityClass {
self.default_class
}
pub fn register(&mut self, node_id: PriorityNodeId, class: PriorityClass, label: &str) {
self.entries
.insert(node_id, NodePriorityEntry::new(node_id, class, label));
}
pub fn register_default(&mut self, node_id: PriorityNodeId, label: &str) {
let class = self.default_class;
self.register(node_id, class, label);
}
pub fn get(&self, node_id: PriorityNodeId) -> Option<&NodePriorityEntry> {
self.entries.get(&node_id)
}
pub fn effective_priority(&self, node_id: PriorityNodeId) -> i32 {
self.entries.get(&node_id).map_or(0, |e| e.effective)
}
pub fn apply_boost(&mut self, node_id: PriorityNodeId, boost: i32) -> bool {
if let Some(entry) = self.entries.get_mut(&node_id) {
entry.boost = boost.clamp(-500, 500);
entry.recalculate();
true
} else {
false
}
}
pub fn set_class(&mut self, node_id: PriorityNodeId, class: PriorityClass) -> bool {
if let Some(entry) = self.entries.get_mut(&node_id) {
entry.class = class;
entry.recalculate();
true
} else {
false
}
}
pub fn unregister(&mut self, node_id: PriorityNodeId) -> bool {
self.entries.remove(&node_id).is_some()
}
pub fn count(&self) -> usize {
self.entries.len()
}
pub fn sorted_by_priority(&self) -> Vec<PriorityNodeId> {
let mut entries: Vec<_> = self.entries.values().collect();
entries.sort_by(|a, b| b.effective.cmp(&a.effective));
entries.iter().map(|e| e.node_id).collect()
}
pub fn nodes_in_class(&self, class: PriorityClass) -> Vec<PriorityNodeId> {
self.entries
.values()
.filter(|e| e.class == class)
.map(|e| e.node_id)
.collect()
}
pub fn promote_class(&mut self, from: PriorityClass, to: PriorityClass) -> usize {
let mut count = 0;
for entry in self.entries.values_mut() {
if entry.class == from {
entry.class = to;
entry.recalculate();
count += 1;
}
}
count
}
pub fn clear(&mut self) {
self.entries.clear();
}
}
impl Default for PriorityManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_priority_class_weight_ordering() {
assert!(PriorityClass::RealTime.weight() > PriorityClass::Interactive.weight());
assert!(PriorityClass::Interactive.weight() > PriorityClass::Normal.weight());
assert!(PriorityClass::Normal.weight() > PriorityClass::Background.weight());
assert!(PriorityClass::Background.weight() > PriorityClass::Idle.weight());
}
#[test]
fn test_priority_class_default() {
assert_eq!(PriorityClass::default(), PriorityClass::Normal);
}
#[test]
fn test_priority_class_display() {
assert_eq!(format!("{}", PriorityClass::RealTime), "RealTime");
assert_eq!(format!("{}", PriorityClass::Idle), "Idle");
}
#[test]
fn test_priority_class_ord() {
assert!(PriorityClass::RealTime > PriorityClass::Normal);
assert!(PriorityClass::Idle < PriorityClass::Background);
}
#[test]
fn test_all_descending() {
let all = PriorityClass::all_descending();
assert_eq!(all.len(), 5);
assert_eq!(all[0], PriorityClass::RealTime);
assert_eq!(all[4], PriorityClass::Idle);
}
#[test]
fn test_node_priority_entry_new() {
let entry =
NodePriorityEntry::new(PriorityNodeId(1), PriorityClass::Interactive, "preview");
assert_eq!(entry.effective, 500);
assert_eq!(entry.boost, 0);
assert_eq!(entry.label, "preview");
}
#[test]
fn test_node_priority_entry_with_boost() {
let entry =
NodePriorityEntry::new(PriorityNodeId(1), PriorityClass::Normal, "n").with_boost(50);
assert_eq!(entry.effective, 150);
assert_eq!(entry.boost, 50);
}
#[test]
fn test_boost_clamp() {
let entry =
NodePriorityEntry::new(PriorityNodeId(1), PriorityClass::Normal, "n").with_boost(9999);
assert_eq!(entry.boost, 500);
}
#[test]
fn test_priority_manager_register() {
let mut mgr = PriorityManager::new();
mgr.register(PriorityNodeId(1), PriorityClass::RealTime, "rt_node");
assert_eq!(mgr.count(), 1);
assert_eq!(mgr.effective_priority(PriorityNodeId(1)), 1000);
}
#[test]
fn test_priority_manager_register_default() {
let mut mgr = PriorityManager::new();
mgr.register_default(PriorityNodeId(1), "default_node");
assert_eq!(mgr.effective_priority(PriorityNodeId(1)), 100);
}
#[test]
fn test_priority_manager_unregistered_returns_zero() {
let mgr = PriorityManager::new();
assert_eq!(mgr.effective_priority(PriorityNodeId(999)), 0);
}
#[test]
fn test_priority_manager_sorted() {
let mut mgr = PriorityManager::new();
mgr.register(PriorityNodeId(1), PriorityClass::Background, "bg");
mgr.register(PriorityNodeId(2), PriorityClass::RealTime, "rt");
mgr.register(PriorityNodeId(3), PriorityClass::Normal, "norm");
let sorted = mgr.sorted_by_priority();
assert_eq!(sorted[0], PriorityNodeId(2)); assert_eq!(sorted[2], PriorityNodeId(1)); }
#[test]
fn test_priority_manager_apply_boost() {
let mut mgr = PriorityManager::new();
mgr.register(PriorityNodeId(1), PriorityClass::Normal, "n");
assert!(mgr.apply_boost(PriorityNodeId(1), 200));
assert_eq!(mgr.effective_priority(PriorityNodeId(1)), 300);
assert!(!mgr.apply_boost(PriorityNodeId(99), 10));
}
#[test]
fn test_priority_manager_set_class() {
let mut mgr = PriorityManager::new();
mgr.register(PriorityNodeId(1), PriorityClass::Normal, "n");
mgr.set_class(PriorityNodeId(1), PriorityClass::RealTime);
assert_eq!(mgr.effective_priority(PriorityNodeId(1)), 1000);
}
#[test]
fn test_priority_manager_promote_class() {
let mut mgr = PriorityManager::new();
mgr.register(PriorityNodeId(1), PriorityClass::Background, "b1");
mgr.register(PriorityNodeId(2), PriorityClass::Background, "b2");
mgr.register(PriorityNodeId(3), PriorityClass::Normal, "n1");
let promoted = mgr.promote_class(PriorityClass::Background, PriorityClass::Normal);
assert_eq!(promoted, 2);
assert_eq!(mgr.effective_priority(PriorityNodeId(1)), 100);
}
#[test]
fn test_priority_manager_nodes_in_class() {
let mut mgr = PriorityManager::new();
mgr.register(PriorityNodeId(1), PriorityClass::Idle, "i1");
mgr.register(PriorityNodeId(2), PriorityClass::Idle, "i2");
mgr.register(PriorityNodeId(3), PriorityClass::Normal, "n1");
let idle_nodes = mgr.nodes_in_class(PriorityClass::Idle);
assert_eq!(idle_nodes.len(), 2);
}
}