1use std::cmp::Ordering;
9use std::collections::HashMap;
10use std::fmt;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub enum PriorityClass {
15 RealTime,
17 Interactive,
19 Normal,
21 Background,
23 Idle,
25}
26
27impl PriorityClass {
28 pub fn weight(&self) -> i32 {
32 match self {
33 Self::RealTime => 1000,
34 Self::Interactive => 500,
35 Self::Normal => 100,
36 Self::Background => 10,
37 Self::Idle => 1,
38 }
39 }
40
41 pub fn all_descending() -> &'static [PriorityClass] {
43 &[
44 PriorityClass::RealTime,
45 PriorityClass::Interactive,
46 PriorityClass::Normal,
47 PriorityClass::Background,
48 PriorityClass::Idle,
49 ]
50 }
51}
52
53impl fmt::Display for PriorityClass {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 match self {
56 Self::RealTime => write!(f, "RealTime"),
57 Self::Interactive => write!(f, "Interactive"),
58 Self::Normal => write!(f, "Normal"),
59 Self::Background => write!(f, "Background"),
60 Self::Idle => write!(f, "Idle"),
61 }
62 }
63}
64
65impl Default for PriorityClass {
66 fn default() -> Self {
67 Self::Normal
68 }
69}
70
71impl Ord for PriorityClass {
72 fn cmp(&self, other: &Self) -> Ordering {
73 self.weight().cmp(&other.weight())
74 }
75}
76
77impl PartialOrd for PriorityClass {
78 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
79 Some(self.cmp(other))
80 }
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
85pub struct PriorityNodeId(pub u64);
86
87impl fmt::Display for PriorityNodeId {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 write!(f, "Node({})", self.0)
90 }
91}
92
93#[derive(Debug, Clone)]
95pub struct NodePriorityEntry {
96 pub node_id: PriorityNodeId,
98 pub class: PriorityClass,
100 pub boost: i32,
102 pub effective: i32,
104 pub label: String,
106}
107
108impl NodePriorityEntry {
109 pub fn new(node_id: PriorityNodeId, class: PriorityClass, label: &str) -> Self {
111 let effective = class.weight();
112 Self {
113 node_id,
114 class,
115 boost: 0,
116 effective,
117 label: label.to_string(),
118 }
119 }
120
121 pub fn with_boost(mut self, boost: i32) -> Self {
123 self.boost = boost.clamp(-500, 500);
124 self.effective = self.class.weight() + self.boost;
125 self
126 }
127
128 pub fn recalculate(&mut self) {
130 self.effective = self.class.weight() + self.boost;
131 }
132}
133
134impl fmt::Display for NodePriorityEntry {
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 write!(
137 f,
138 "{} [{}] priority={} ({}{})",
139 self.label,
140 self.node_id,
141 self.effective,
142 self.class,
143 if self.boost != 0 {
144 format!("{:+}", self.boost)
145 } else {
146 String::new()
147 }
148 )
149 }
150}
151
152pub struct PriorityManager {
154 entries: HashMap<PriorityNodeId, NodePriorityEntry>,
156 default_class: PriorityClass,
158}
159
160impl PriorityManager {
161 pub fn new() -> Self {
163 Self {
164 entries: HashMap::new(),
165 default_class: PriorityClass::Normal,
166 }
167 }
168
169 pub fn set_default_class(&mut self, class: PriorityClass) {
171 self.default_class = class;
172 }
173
174 pub fn default_class(&self) -> PriorityClass {
176 self.default_class
177 }
178
179 pub fn register(&mut self, node_id: PriorityNodeId, class: PriorityClass, label: &str) {
181 self.entries
182 .insert(node_id, NodePriorityEntry::new(node_id, class, label));
183 }
184
185 pub fn register_default(&mut self, node_id: PriorityNodeId, label: &str) {
187 let class = self.default_class;
188 self.register(node_id, class, label);
189 }
190
191 pub fn get(&self, node_id: PriorityNodeId) -> Option<&NodePriorityEntry> {
193 self.entries.get(&node_id)
194 }
195
196 pub fn effective_priority(&self, node_id: PriorityNodeId) -> i32 {
198 self.entries.get(&node_id).map_or(0, |e| e.effective)
199 }
200
201 pub fn apply_boost(&mut self, node_id: PriorityNodeId, boost: i32) -> bool {
203 if let Some(entry) = self.entries.get_mut(&node_id) {
204 entry.boost = boost.clamp(-500, 500);
205 entry.recalculate();
206 true
207 } else {
208 false
209 }
210 }
211
212 pub fn set_class(&mut self, node_id: PriorityNodeId, class: PriorityClass) -> bool {
214 if let Some(entry) = self.entries.get_mut(&node_id) {
215 entry.class = class;
216 entry.recalculate();
217 true
218 } else {
219 false
220 }
221 }
222
223 pub fn unregister(&mut self, node_id: PriorityNodeId) -> bool {
225 self.entries.remove(&node_id).is_some()
226 }
227
228 pub fn count(&self) -> usize {
230 self.entries.len()
231 }
232
233 pub fn sorted_by_priority(&self) -> Vec<PriorityNodeId> {
235 let mut entries: Vec<_> = self.entries.values().collect();
236 entries.sort_by(|a, b| b.effective.cmp(&a.effective));
237 entries.iter().map(|e| e.node_id).collect()
238 }
239
240 pub fn nodes_in_class(&self, class: PriorityClass) -> Vec<PriorityNodeId> {
242 self.entries
243 .values()
244 .filter(|e| e.class == class)
245 .map(|e| e.node_id)
246 .collect()
247 }
248
249 pub fn promote_class(&mut self, from: PriorityClass, to: PriorityClass) -> usize {
251 let mut count = 0;
252 for entry in self.entries.values_mut() {
253 if entry.class == from {
254 entry.class = to;
255 entry.recalculate();
256 count += 1;
257 }
258 }
259 count
260 }
261
262 pub fn clear(&mut self) {
264 self.entries.clear();
265 }
266}
267
268impl Default for PriorityManager {
269 fn default() -> Self {
270 Self::new()
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277
278 #[test]
279 fn test_priority_class_weight_ordering() {
280 assert!(PriorityClass::RealTime.weight() > PriorityClass::Interactive.weight());
281 assert!(PriorityClass::Interactive.weight() > PriorityClass::Normal.weight());
282 assert!(PriorityClass::Normal.weight() > PriorityClass::Background.weight());
283 assert!(PriorityClass::Background.weight() > PriorityClass::Idle.weight());
284 }
285
286 #[test]
287 fn test_priority_class_default() {
288 assert_eq!(PriorityClass::default(), PriorityClass::Normal);
289 }
290
291 #[test]
292 fn test_priority_class_display() {
293 assert_eq!(format!("{}", PriorityClass::RealTime), "RealTime");
294 assert_eq!(format!("{}", PriorityClass::Idle), "Idle");
295 }
296
297 #[test]
298 fn test_priority_class_ord() {
299 assert!(PriorityClass::RealTime > PriorityClass::Normal);
300 assert!(PriorityClass::Idle < PriorityClass::Background);
301 }
302
303 #[test]
304 fn test_all_descending() {
305 let all = PriorityClass::all_descending();
306 assert_eq!(all.len(), 5);
307 assert_eq!(all[0], PriorityClass::RealTime);
308 assert_eq!(all[4], PriorityClass::Idle);
309 }
310
311 #[test]
312 fn test_node_priority_entry_new() {
313 let entry =
314 NodePriorityEntry::new(PriorityNodeId(1), PriorityClass::Interactive, "preview");
315 assert_eq!(entry.effective, 500);
316 assert_eq!(entry.boost, 0);
317 assert_eq!(entry.label, "preview");
318 }
319
320 #[test]
321 fn test_node_priority_entry_with_boost() {
322 let entry =
323 NodePriorityEntry::new(PriorityNodeId(1), PriorityClass::Normal, "n").with_boost(50);
324 assert_eq!(entry.effective, 150);
325 assert_eq!(entry.boost, 50);
326 }
327
328 #[test]
329 fn test_boost_clamp() {
330 let entry =
331 NodePriorityEntry::new(PriorityNodeId(1), PriorityClass::Normal, "n").with_boost(9999);
332 assert_eq!(entry.boost, 500);
333 }
334
335 #[test]
336 fn test_priority_manager_register() {
337 let mut mgr = PriorityManager::new();
338 mgr.register(PriorityNodeId(1), PriorityClass::RealTime, "rt_node");
339 assert_eq!(mgr.count(), 1);
340 assert_eq!(mgr.effective_priority(PriorityNodeId(1)), 1000);
341 }
342
343 #[test]
344 fn test_priority_manager_register_default() {
345 let mut mgr = PriorityManager::new();
346 mgr.register_default(PriorityNodeId(1), "default_node");
347 assert_eq!(mgr.effective_priority(PriorityNodeId(1)), 100);
348 }
349
350 #[test]
351 fn test_priority_manager_unregistered_returns_zero() {
352 let mgr = PriorityManager::new();
353 assert_eq!(mgr.effective_priority(PriorityNodeId(999)), 0);
354 }
355
356 #[test]
357 fn test_priority_manager_sorted() {
358 let mut mgr = PriorityManager::new();
359 mgr.register(PriorityNodeId(1), PriorityClass::Background, "bg");
360 mgr.register(PriorityNodeId(2), PriorityClass::RealTime, "rt");
361 mgr.register(PriorityNodeId(3), PriorityClass::Normal, "norm");
362 let sorted = mgr.sorted_by_priority();
363 assert_eq!(sorted[0], PriorityNodeId(2)); assert_eq!(sorted[2], PriorityNodeId(1)); }
366
367 #[test]
368 fn test_priority_manager_apply_boost() {
369 let mut mgr = PriorityManager::new();
370 mgr.register(PriorityNodeId(1), PriorityClass::Normal, "n");
371 assert!(mgr.apply_boost(PriorityNodeId(1), 200));
372 assert_eq!(mgr.effective_priority(PriorityNodeId(1)), 300);
373 assert!(!mgr.apply_boost(PriorityNodeId(99), 10));
374 }
375
376 #[test]
377 fn test_priority_manager_set_class() {
378 let mut mgr = PriorityManager::new();
379 mgr.register(PriorityNodeId(1), PriorityClass::Normal, "n");
380 mgr.set_class(PriorityNodeId(1), PriorityClass::RealTime);
381 assert_eq!(mgr.effective_priority(PriorityNodeId(1)), 1000);
382 }
383
384 #[test]
385 fn test_priority_manager_promote_class() {
386 let mut mgr = PriorityManager::new();
387 mgr.register(PriorityNodeId(1), PriorityClass::Background, "b1");
388 mgr.register(PriorityNodeId(2), PriorityClass::Background, "b2");
389 mgr.register(PriorityNodeId(3), PriorityClass::Normal, "n1");
390 let promoted = mgr.promote_class(PriorityClass::Background, PriorityClass::Normal);
391 assert_eq!(promoted, 2);
392 assert_eq!(mgr.effective_priority(PriorityNodeId(1)), 100);
393 }
394
395 #[test]
396 fn test_priority_manager_nodes_in_class() {
397 let mut mgr = PriorityManager::new();
398 mgr.register(PriorityNodeId(1), PriorityClass::Idle, "i1");
399 mgr.register(PriorityNodeId(2), PriorityClass::Idle, "i2");
400 mgr.register(PriorityNodeId(3), PriorityClass::Normal, "n1");
401 let idle_nodes = mgr.nodes_in_class(PriorityClass::Idle);
402 assert_eq!(idle_nodes.len(), 2);
403 }
404}