use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::collections::{BTreeMap, VecDeque};
use std::fs::File;
use std::io::{self, BufReader, BufWriter};
use std::path::Path;
#[derive(Debug, Serialize, Deserialize)]
pub struct PriorityQueue<P, T>
where
P: Ord,
{
map: BTreeMap<P, VecDeque<T>>,
capacity: usize,
size: usize,
}
impl<P, T> PriorityQueue<P, T>
where
P: Ord + Clone + Serialize + DeserializeOwned,
T: Serialize + DeserializeOwned,
{
pub fn new(capacity: usize) -> Self {
Self {
map: BTreeMap::new(),
capacity,
size: 0,
}
}
pub fn enqueue(&mut self, priority: P, item: T) {
self.map.entry(priority).or_default().push_back(item);
self.size += 1;
if self.size > self.capacity {
self.drop_lowest_priority();
}
}
pub fn dequeue(&mut self) -> Option<(P, T)> {
if self.size == 0 {
return None;
}
let mut entry = self.map.last_entry()?;
let prio = entry.key().clone();
let deque = entry.get_mut();
let item = deque.pop_front()?;
if deque.is_empty() {
entry.remove();
}
self.size -= 1;
Some((prio, item))
}
#[must_use]
pub fn peek(&self) -> Option<(&P, &T)> {
let (prio, deque) = self.map.last_key_value()?;
let item = deque.front()?;
Some((prio, item))
}
pub fn len(&self) -> usize {
self.size
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.size == 0
}
pub fn clear(&mut self) {
self.map.clear();
self.size = 0;
}
pub fn save_to_file<Q: AsRef<Path>>(&self, path: Q) -> io::Result<()> {
let file = File::create(path)?;
let writer = BufWriter::new(file);
serde_json::to_writer(writer, self)?;
Ok(())
}
pub fn load_from_file<Q: AsRef<Path>>(path: Q) -> io::Result<Self> {
let file = File::open(path)?;
let reader = BufReader::new(file);
let queue: Self = serde_json::from_reader(reader)?;
Ok(queue)
}
fn drop_lowest_priority(&mut self) {
if let Some(mut entry) = self.map.first_entry() {
let deque = entry.get_mut();
deque.pop_front();
if deque.is_empty() {
entry.remove();
}
self.size -= 1;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
#[test]
fn test_enqueue_dequeue_order() {
let mut pq = PriorityQueue::new(10);
pq.enqueue(1, "low".to_string());
pq.enqueue(3, "high".to_string());
pq.enqueue(2, "mid".to_string());
assert_eq!(pq.dequeue(), Some((3, "high".to_string())));
assert_eq!(pq.dequeue(), Some((2, "mid".to_string())));
assert_eq!(pq.dequeue(), Some((1, "low".to_string())));
assert_eq!(pq.dequeue(), None);
}
#[test]
fn test_fifo_same_priority() {
let mut pq = PriorityQueue::new(10);
pq.enqueue(2, "first".to_string());
pq.enqueue(2, "second".to_string());
pq.enqueue(1, "low".to_string());
assert_eq!(pq.dequeue(), Some((2, "first".to_string())));
assert_eq!(pq.dequeue(), Some((2, "second".to_string())));
assert_eq!(pq.dequeue(), Some((1, "low".to_string())));
}
#[test]
fn test_capacity_eviction_low_incoming() {
let mut pq = PriorityQueue::new(2);
pq.enqueue(10, "high".to_string());
pq.enqueue(5, "mid".to_string());
pq.enqueue(1, "low-dropped".to_string());
assert_eq!(pq.len(), 2);
assert_eq!(pq.dequeue(), Some((10, "high".to_string())));
assert_eq!(pq.dequeue(), Some((5, "mid".to_string())));
assert_eq!(pq.dequeue(), None);
}
#[test]
fn test_capacity_eviction_mid_incoming() {
let mut pq = PriorityQueue::new(2);
pq.enqueue(10, "high".to_string());
pq.enqueue(1, "low".to_string());
pq.enqueue(5, "mid".to_string());
assert_eq!(pq.len(), 2);
assert_eq!(pq.dequeue(), Some((10, "high".to_string())));
assert_eq!(pq.dequeue(), Some((5, "mid".to_string())));
}
#[test]
fn test_peek() {
let mut pq = PriorityQueue::new(10);
pq.enqueue(1, "a".to_string());
pq.enqueue(2, "b".to_string());
{
let (p, v) = pq.peek().unwrap();
assert_eq!(*p, 2);
assert_eq!(v, "b");
}
assert_eq!(pq.len(), 2);
pq.dequeue();
{
let (p, v) = pq.peek().unwrap();
assert_eq!(*p, 1);
assert_eq!(v, "a");
}
}
#[test]
fn test_persistence() {
let mut pq = PriorityQueue::new(5);
pq.enqueue(1, "p1".to_string());
pq.enqueue(2, "p2".to_string());
let path = "test_pq_persistence.json";
pq.save_to_file(path).unwrap();
let mut pq2 = PriorityQueue::<i32, String>::load_from_file(path).unwrap();
assert_eq!(pq2.capacity, 5);
assert_eq!(pq2.len(), 2);
assert_eq!(pq2.dequeue(), Some((2, "p2".to_string())));
assert_eq!(pq2.dequeue(), Some((1, "p1".to_string())));
let _ = fs::remove_file(path);
}
}