use itertools::Itertools;
use super::PackId;
use crate::{Error, Result};
#[derive(Debug, PartialEq, Eq)]
pub struct PackIdAlloc {
next: PackId,
reuse_pool: Vec<PackId>,
}
impl Default for PackIdAlloc {
fn default() -> Self {
Self {
next: PackId::new(1), reuse_pool: vec![],
}
}
}
impl std::fmt::Display for PackIdAlloc {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let ids = self
.reuse_pool
.iter()
.chain(std::iter::once(&self.next))
.map(|item| item.to_string())
.join(" ");
write!(f, "{ids}")
}
}
impl std::str::FromStr for PackIdAlloc {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
let mut ids: Vec<PackId> = s
.split_whitespace()
.map(|item| item.parse())
.collect::<Result<_>>()?;
let Some(next) = ids.pop() else {
return Err(Error::InvalidFormat(
"PackIdAlloc cannot be empty, expected format: 'reuse_ids... next_id'".into(),
));
};
Ok(Self {
next,
reuse_pool: ids,
})
}
}
impl PackIdAlloc {
pub const HOT_PACK_ID: PackId = PackId::new(0);
pub fn next_id(&mut self) -> PackId {
if let Some(id) = self.reuse_pool.pop() {
return id;
}
let next = self.next;
self.next = next + 1;
next
}
pub fn add_id(&mut self, id: PackId) {
if id != Self::HOT_PACK_ID && id < self.next {
self.reuse_pool.push(id);
}
}
}
#[cfg(test)]
mod test {
use super::PackIdAlloc;
#[test]
fn test_id_alloc() {
let mut alloc = PackIdAlloc::default();
assert!(alloc.next > PackIdAlloc::HOT_PACK_ID);
let next = alloc.next_id();
alloc.add_id(PackIdAlloc::HOT_PACK_ID);
alloc.add_id(next + 10);
alloc.add_id(next);
assert!(!alloc.reuse_pool.contains(&PackIdAlloc::HOT_PACK_ID));
assert!(!alloc.reuse_pool.iter().any(|item| item > &alloc.next));
let other_alloc: PackIdAlloc = alloc.to_string().parse().expect("should ok");
assert_eq!(other_alloc, alloc);
}
}