1use std::collections::{BTreeMap, VecDeque};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
20#[repr(u8)]
21pub enum RequestPriority {
22 Low = 0,
24 Normal = 1,
26 High = 2,
28 Critical = 3,
30}
31
32#[derive(Debug, Clone)]
36pub struct PeerRequest {
37 pub id: u64,
39 pub peer_id: String,
41 pub priority: RequestPriority,
43 pub payload_size: u64,
45 pub enqueued_tick: u64,
47}
48
49#[derive(Debug, Clone)]
53pub struct PriorityQueueStats {
54 pub total_enqueued: u64,
56 pub total_dequeued: u64,
58 pub current_size: usize,
60 pub critical_count: usize,
62 pub high_count: usize,
64 pub normal_count: usize,
66 pub low_count: usize,
68}
69
70pub struct PeerRequestQueue {
78 queues: BTreeMap<RequestPriority, VecDeque<PeerRequest>>,
80 next_id: u64,
82 current_tick: u64,
84 total_enqueued: u64,
86 total_dequeued: u64,
88}
89
90impl PeerRequestQueue {
91 pub fn new() -> Self {
93 let mut queues = BTreeMap::new();
94 queues.insert(RequestPriority::Critical, VecDeque::new());
95 queues.insert(RequestPriority::High, VecDeque::new());
96 queues.insert(RequestPriority::Normal, VecDeque::new());
97 queues.insert(RequestPriority::Low, VecDeque::new());
98
99 Self {
100 queues,
101 next_id: 0,
102 current_tick: 0,
103 total_enqueued: 0,
104 total_dequeued: 0,
105 }
106 }
107
108 pub fn enqueue(&mut self, peer_id: &str, priority: RequestPriority, payload_size: u64) -> u64 {
110 let id = self.next_id;
111 self.next_id = self.next_id.wrapping_add(1);
112
113 let request = PeerRequest {
114 id,
115 peer_id: peer_id.to_string(),
116 priority,
117 payload_size,
118 enqueued_tick: self.current_tick,
119 };
120
121 if let Some(q) = self.queues.get_mut(&priority) {
123 q.push_back(request);
124 }
125
126 self.total_enqueued += 1;
127 id
128 }
129
130 pub fn dequeue(&mut self) -> Option<PeerRequest> {
134 for (_, q) in self.queues.iter_mut().rev() {
137 if let Some(req) = q.pop_front() {
138 self.total_dequeued += 1;
139 return Some(req);
140 }
141 }
142 None
143 }
144
145 pub fn peek(&self) -> Option<&PeerRequest> {
147 for (_, q) in self.queues.iter().rev() {
148 if let Some(req) = q.front() {
149 return Some(req);
150 }
151 }
152 None
153 }
154
155 pub fn len(&self) -> usize {
157 self.queues.values().map(VecDeque::len).sum()
158 }
159
160 pub fn is_empty(&self) -> bool {
162 self.queues.values().all(VecDeque::is_empty)
163 }
164
165 pub fn drain_priority(&mut self, priority: RequestPriority) -> Vec<PeerRequest> {
167 match self.queues.get_mut(&priority) {
168 Some(q) => {
169 let drained: Vec<PeerRequest> = q.drain(..).collect();
170 self.total_dequeued += drained.len() as u64;
171 drained
172 }
173 None => Vec::new(),
174 }
175 }
176
177 pub fn cancel(&mut self, request_id: u64) -> bool {
181 for (_, q) in self.queues.iter_mut() {
182 if let Some(pos) = q.iter().position(|r| r.id == request_id) {
183 q.remove(pos);
184 return true;
185 }
186 }
187 false
188 }
189
190 pub fn tick(&mut self) {
192 self.current_tick = self.current_tick.wrapping_add(1);
193 }
194
195 pub fn count_by_priority(&self, priority: RequestPriority) -> usize {
197 self.queues.get(&priority).map_or(0, VecDeque::len)
198 }
199
200 pub fn total_payload_bytes(&self) -> u64 {
202 self.queues
203 .values()
204 .flat_map(|q| q.iter())
205 .map(|r| r.payload_size)
206 .sum()
207 }
208
209 pub fn stats(&self) -> PriorityQueueStats {
211 PriorityQueueStats {
212 total_enqueued: self.total_enqueued,
213 total_dequeued: self.total_dequeued,
214 current_size: self.len(),
215 critical_count: self.count_by_priority(RequestPriority::Critical),
216 high_count: self.count_by_priority(RequestPriority::High),
217 normal_count: self.count_by_priority(RequestPriority::Normal),
218 low_count: self.count_by_priority(RequestPriority::Low),
219 }
220 }
221}
222
223impl Default for PeerRequestQueue {
224 fn default() -> Self {
225 Self::new()
226 }
227}
228
229#[cfg(test)]
232mod tests {
233 use super::*;
234
235 fn new_queue() -> PeerRequestQueue {
238 PeerRequestQueue::new()
239 }
240
241 #[test]
244 fn enqueue_returns_unique_ids() {
245 let mut q = new_queue();
246 let id0 = q.enqueue("peer-a", RequestPriority::Normal, 100);
247 let id1 = q.enqueue("peer-a", RequestPriority::Normal, 200);
248 assert_ne!(id0, id1);
249 }
250
251 #[test]
252 fn dequeue_returns_none_when_empty() {
253 let mut q = new_queue();
254 assert!(q.dequeue().is_none());
255 }
256
257 #[test]
258 fn enqueue_then_dequeue_returns_same_request() {
259 let mut q = new_queue();
260 let id = q.enqueue("peer-a", RequestPriority::Normal, 512);
261 let req = q.dequeue().expect("should have one request");
262 assert_eq!(req.id, id);
263 assert_eq!(req.peer_id, "peer-a");
264 assert_eq!(req.payload_size, 512);
265 }
266
267 #[test]
270 fn critical_dequeued_before_high() {
271 let mut q = new_queue();
272 q.enqueue("peer-a", RequestPriority::High, 10);
273 q.enqueue("peer-a", RequestPriority::Critical, 10);
274 let first = q.dequeue().expect("should have request");
275 assert_eq!(first.priority, RequestPriority::Critical);
276 }
277
278 #[test]
279 fn high_dequeued_before_normal() {
280 let mut q = new_queue();
281 q.enqueue("peer-a", RequestPriority::Normal, 10);
282 q.enqueue("peer-a", RequestPriority::High, 10);
283 let first = q.dequeue().expect("should have request");
284 assert_eq!(first.priority, RequestPriority::High);
285 }
286
287 #[test]
288 fn normal_dequeued_before_low() {
289 let mut q = new_queue();
290 q.enqueue("peer-a", RequestPriority::Low, 10);
291 q.enqueue("peer-a", RequestPriority::Normal, 10);
292 let first = q.dequeue().expect("should have request");
293 assert_eq!(first.priority, RequestPriority::Normal);
294 }
295
296 #[test]
297 fn full_priority_ordering() {
298 let mut q = new_queue();
299 q.enqueue("peer-a", RequestPriority::Low, 10);
300 q.enqueue("peer-a", RequestPriority::Normal, 10);
301 q.enqueue("peer-a", RequestPriority::Critical, 10);
302 q.enqueue("peer-a", RequestPriority::High, 10);
303
304 let r1 = q.dequeue().expect("msg");
305 let r2 = q.dequeue().expect("msg");
306 let r3 = q.dequeue().expect("msg");
307 let r4 = q.dequeue().expect("msg");
308
309 assert_eq!(r1.priority, RequestPriority::Critical);
310 assert_eq!(r2.priority, RequestPriority::High);
311 assert_eq!(r3.priority, RequestPriority::Normal);
312 assert_eq!(r4.priority, RequestPriority::Low);
313 assert!(q.dequeue().is_none());
314 }
315
316 #[test]
319 fn fifo_within_critical() {
320 let mut q = new_queue();
321 let id0 = q.enqueue("peer-a", RequestPriority::Critical, 10);
322 let id1 = q.enqueue("peer-a", RequestPriority::Critical, 20);
323 let id2 = q.enqueue("peer-a", RequestPriority::Critical, 30);
324 assert_eq!(q.dequeue().expect("msg").id, id0);
325 assert_eq!(q.dequeue().expect("msg").id, id1);
326 assert_eq!(q.dequeue().expect("msg").id, id2);
327 }
328
329 #[test]
330 fn fifo_within_high() {
331 let mut q = new_queue();
332 let id0 = q.enqueue("peer-b", RequestPriority::High, 10);
333 let id1 = q.enqueue("peer-b", RequestPriority::High, 20);
334 assert_eq!(q.dequeue().expect("msg").id, id0);
335 assert_eq!(q.dequeue().expect("msg").id, id1);
336 }
337
338 #[test]
339 fn fifo_within_normal() {
340 let mut q = new_queue();
341 let id0 = q.enqueue("peer-c", RequestPriority::Normal, 10);
342 let id1 = q.enqueue("peer-c", RequestPriority::Normal, 20);
343 let id2 = q.enqueue("peer-c", RequestPriority::Normal, 30);
344 assert_eq!(q.dequeue().expect("msg").id, id0);
345 assert_eq!(q.dequeue().expect("msg").id, id1);
346 assert_eq!(q.dequeue().expect("msg").id, id2);
347 }
348
349 #[test]
350 fn fifo_within_low() {
351 let mut q = new_queue();
352 let id0 = q.enqueue("peer-d", RequestPriority::Low, 10);
353 let id1 = q.enqueue("peer-d", RequestPriority::Low, 20);
354 assert_eq!(q.dequeue().expect("msg").id, id0);
355 assert_eq!(q.dequeue().expect("msg").id, id1);
356 }
357
358 #[test]
361 fn peek_returns_none_when_empty() {
362 let q = new_queue();
363 assert!(q.peek().is_none());
364 }
365
366 #[test]
367 fn peek_does_not_remove() {
368 let mut q = new_queue();
369 q.enqueue("peer-a", RequestPriority::Normal, 100);
370 assert!(q.peek().is_some());
371 assert_eq!(q.len(), 1);
372 assert!(q.peek().is_some());
373 assert_eq!(q.len(), 1);
374 }
375
376 #[test]
377 fn peek_returns_highest_priority() {
378 let mut q = new_queue();
379 q.enqueue("peer-a", RequestPriority::Low, 10);
380 q.enqueue("peer-a", RequestPriority::High, 20);
381 let peeked = q.peek().expect("should have request");
382 assert_eq!(peeked.priority, RequestPriority::High);
383 }
384
385 #[test]
388 fn len_counts_all_priorities() {
389 let mut q = new_queue();
390 q.enqueue("peer-a", RequestPriority::Critical, 10);
391 q.enqueue("peer-a", RequestPriority::High, 10);
392 q.enqueue("peer-a", RequestPriority::Normal, 10);
393 q.enqueue("peer-a", RequestPriority::Low, 10);
394 assert_eq!(q.len(), 4);
395 }
396
397 #[test]
398 fn is_empty_when_new() {
399 let q = new_queue();
400 assert!(q.is_empty());
401 }
402
403 #[test]
404 fn is_empty_false_after_enqueue() {
405 let mut q = new_queue();
406 q.enqueue("peer-a", RequestPriority::Normal, 10);
407 assert!(!q.is_empty());
408 }
409
410 #[test]
411 fn is_empty_true_after_all_dequeued() {
412 let mut q = new_queue();
413 q.enqueue("peer-a", RequestPriority::Normal, 10);
414 let _ = q.dequeue();
415 assert!(q.is_empty());
416 }
417
418 #[test]
421 fn drain_priority_removes_all_at_level() {
422 let mut q = new_queue();
423 q.enqueue("peer-a", RequestPriority::High, 10);
424 q.enqueue("peer-b", RequestPriority::High, 20);
425 q.enqueue("peer-a", RequestPriority::Normal, 30);
426
427 let drained = q.drain_priority(RequestPriority::High);
428 assert_eq!(drained.len(), 2);
429 assert_eq!(q.len(), 1);
430 assert_eq!(q.count_by_priority(RequestPriority::High), 0);
431 }
432
433 #[test]
434 fn drain_priority_returns_empty_when_none() {
435 let mut q = new_queue();
436 q.enqueue("peer-a", RequestPriority::Normal, 10);
437 let drained = q.drain_priority(RequestPriority::Critical);
438 assert!(drained.is_empty());
439 }
440
441 #[test]
442 fn drain_preserves_fifo_order() {
443 let mut q = new_queue();
444 let id0 = q.enqueue("peer-a", RequestPriority::Normal, 10);
445 let id1 = q.enqueue("peer-b", RequestPriority::Normal, 20);
446 let id2 = q.enqueue("peer-c", RequestPriority::Normal, 30);
447
448 let drained = q.drain_priority(RequestPriority::Normal);
449 assert_eq!(drained.len(), 3);
450 assert_eq!(drained[0].id, id0);
451 assert_eq!(drained[1].id, id1);
452 assert_eq!(drained[2].id, id2);
453 }
454
455 #[test]
458 fn cancel_removes_specific_request() {
459 let mut q = new_queue();
460 let _id0 = q.enqueue("peer-a", RequestPriority::Normal, 10);
461 let id1 = q.enqueue("peer-b", RequestPriority::Normal, 20);
462 let _id2 = q.enqueue("peer-c", RequestPriority::Normal, 30);
463
464 assert!(q.cancel(id1));
465 assert_eq!(q.len(), 2);
466
467 let r0 = q.dequeue().expect("msg");
469 let r2 = q.dequeue().expect("msg");
470 assert_eq!(r0.id, _id0);
471 assert_eq!(r2.id, _id2);
472 }
473
474 #[test]
475 fn cancel_returns_false_for_nonexistent_id() {
476 let mut q = new_queue();
477 q.enqueue("peer-a", RequestPriority::Normal, 10);
478 assert!(!q.cancel(9999));
479 assert_eq!(q.len(), 1);
480 }
481
482 #[test]
483 fn cancel_across_priorities() {
484 let mut q = new_queue();
485 let _id0 = q.enqueue("peer-a", RequestPriority::Critical, 10);
486 let id1 = q.enqueue("peer-b", RequestPriority::Low, 20);
487
488 assert!(q.cancel(id1));
489 assert_eq!(q.len(), 1);
490 let remaining = q.dequeue().expect("msg");
491 assert_eq!(remaining.priority, RequestPriority::Critical);
492 }
493
494 #[test]
497 fn tick_advances_clock() {
498 let mut q = new_queue();
499 q.tick();
500 q.tick();
501 let id = q.enqueue("peer-a", RequestPriority::Normal, 10);
502 let req = q.dequeue().expect("msg");
503 assert_eq!(req.id, id);
504 assert_eq!(req.enqueued_tick, 2);
505 }
506
507 #[test]
508 fn enqueued_tick_reflects_current_clock() {
509 let mut q = new_queue();
510 q.enqueue("peer-a", RequestPriority::Normal, 10);
511 q.tick();
512 q.tick();
513 q.tick();
514 q.enqueue("peer-b", RequestPriority::Normal, 20);
515
516 let r0 = q.dequeue().expect("msg");
517 let r1 = q.dequeue().expect("msg");
518 assert_eq!(r0.enqueued_tick, 0);
519 assert_eq!(r1.enqueued_tick, 3);
520 }
521
522 #[test]
525 fn count_by_priority_tracks_correctly() {
526 let mut q = new_queue();
527 q.enqueue("peer-a", RequestPriority::Critical, 10);
528 q.enqueue("peer-b", RequestPriority::Critical, 10);
529 q.enqueue("peer-c", RequestPriority::Normal, 10);
530
531 assert_eq!(q.count_by_priority(RequestPriority::Critical), 2);
532 assert_eq!(q.count_by_priority(RequestPriority::High), 0);
533 assert_eq!(q.count_by_priority(RequestPriority::Normal), 1);
534 assert_eq!(q.count_by_priority(RequestPriority::Low), 0);
535 }
536
537 #[test]
540 fn total_payload_bytes_sums_all() {
541 let mut q = new_queue();
542 q.enqueue("peer-a", RequestPriority::Critical, 100);
543 q.enqueue("peer-b", RequestPriority::Normal, 200);
544 q.enqueue("peer-c", RequestPriority::Low, 300);
545 assert_eq!(q.total_payload_bytes(), 600);
546 }
547
548 #[test]
549 fn total_payload_bytes_decreases_after_dequeue() {
550 let mut q = new_queue();
551 q.enqueue("peer-a", RequestPriority::Normal, 500);
552 q.enqueue("peer-b", RequestPriority::Normal, 300);
553 let _ = q.dequeue();
554 assert_eq!(q.total_payload_bytes(), 300);
555 }
556
557 #[test]
558 fn total_payload_bytes_zero_when_empty() {
559 let q = new_queue();
560 assert_eq!(q.total_payload_bytes(), 0);
561 }
562
563 #[test]
566 fn stats_tracks_enqueued_and_dequeued() {
567 let mut q = new_queue();
568 q.enqueue("peer-a", RequestPriority::Normal, 10);
569 q.enqueue("peer-b", RequestPriority::High, 20);
570 q.enqueue("peer-c", RequestPriority::Low, 30);
571 let _ = q.dequeue();
572
573 let s = q.stats();
574 assert_eq!(s.total_enqueued, 3);
575 assert_eq!(s.total_dequeued, 1);
576 assert_eq!(s.current_size, 2);
577 }
578
579 #[test]
580 fn stats_priority_counts() {
581 let mut q = new_queue();
582 q.enqueue("peer-a", RequestPriority::Critical, 10);
583 q.enqueue("peer-b", RequestPriority::Critical, 10);
584 q.enqueue("peer-c", RequestPriority::High, 10);
585 q.enqueue("peer-d", RequestPriority::Normal, 10);
586 q.enqueue("peer-e", RequestPriority::Low, 10);
587 q.enqueue("peer-f", RequestPriority::Low, 10);
588
589 let s = q.stats();
590 assert_eq!(s.critical_count, 2);
591 assert_eq!(s.high_count, 1);
592 assert_eq!(s.normal_count, 1);
593 assert_eq!(s.low_count, 2);
594 }
595
596 #[test]
597 fn stats_after_drain() {
598 let mut q = new_queue();
599 q.enqueue("peer-a", RequestPriority::High, 10);
600 q.enqueue("peer-b", RequestPriority::High, 10);
601 q.enqueue("peer-c", RequestPriority::Low, 10);
602
603 let _ = q.drain_priority(RequestPriority::High);
604 let s = q.stats();
605 assert_eq!(s.total_dequeued, 2);
606 assert_eq!(s.high_count, 0);
607 assert_eq!(s.low_count, 1);
608 assert_eq!(s.current_size, 1);
609 }
610
611 #[test]
614 fn mixed_enqueue_dequeue_cancel_ordering() {
615 let mut q = new_queue();
616 let id_low = q.enqueue("peer-a", RequestPriority::Low, 10);
617 let _id_crit = q.enqueue("peer-b", RequestPriority::Critical, 20);
618 let id_normal = q.enqueue("peer-c", RequestPriority::Normal, 30);
619
620 assert!(q.cancel(id_normal));
622 assert_eq!(q.len(), 2);
623
624 let r1 = q.dequeue().expect("msg");
626 assert_eq!(r1.priority, RequestPriority::Critical);
627 let r2 = q.dequeue().expect("msg");
628 assert_eq!(r2.id, id_low);
629 assert!(q.is_empty());
630 }
631
632 #[test]
633 fn interleaved_enqueue_dequeue() {
634 let mut q = new_queue();
635 q.enqueue("peer-a", RequestPriority::Normal, 10);
636 q.enqueue("peer-b", RequestPriority::High, 20);
637
638 let r1 = q.dequeue().expect("msg");
640 assert_eq!(r1.priority, RequestPriority::High);
641
642 q.enqueue("peer-c", RequestPriority::Critical, 30);
644
645 let r2 = q.dequeue().expect("msg");
647 assert_eq!(r2.priority, RequestPriority::Critical);
648
649 let r3 = q.dequeue().expect("msg");
651 assert_eq!(r3.priority, RequestPriority::Normal);
652 assert!(q.is_empty());
653 }
654
655 #[test]
656 fn multiple_peers_same_priority() {
657 let mut q = new_queue();
658 let id_a = q.enqueue("peer-a", RequestPriority::Normal, 10);
659 let id_b = q.enqueue("peer-b", RequestPriority::Normal, 20);
660 let id_c = q.enqueue("peer-c", RequestPriority::Normal, 30);
661
662 assert_eq!(q.dequeue().expect("msg").id, id_a);
664 assert_eq!(q.dequeue().expect("msg").id, id_b);
665 assert_eq!(q.dequeue().expect("msg").id, id_c);
666 }
667
668 #[test]
669 fn default_impl_creates_empty_queue() {
670 let q = PeerRequestQueue::default();
671 assert!(q.is_empty());
672 assert_eq!(q.len(), 0);
673 assert_eq!(q.total_payload_bytes(), 0);
674 }
675}