use num::{Bounded, Integer, Unsigned};
use std::collections::HashMap;
use std::hash::Hash;
pub trait IdValue: Unsigned + Integer + Copy + Hash + Bounded {}
impl<T> IdValue for T where T: Unsigned + Integer + Copy + Hash + Bounded {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum IdError {
Duplicate,
NonExistent,
MaximumNumberCreated
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct IdRef<T>(T);
pub struct IdManager<T: IdValue> {
next_id_value: T,
ids: HashMap<String, T>,
max_created: bool
}
impl<T: IdValue> IdManager<T> {
pub fn new() -> IdManager<T> {
let next_id_value: T = T::zero();
let ids = HashMap::new();
IdManager {
next_id_value,
ids,
max_created: false
}
}
pub fn create(&mut self, id_str: &str) -> Result<IdRef<T>, IdError> {
if self.ids.contains_key(id_str) {
Err(IdError::Duplicate)
} else if self.max_created {
Err(IdError::MaximumNumberCreated)
} else {
let id = IdRef::<T>(self.next_id_value);
self.ids.insert(id_str.to_string(), self.next_id_value);
if self.next_id_value < T::max_value() {
self.next_id_value = self.next_id_value + T::one();
} else {
self.max_created = true;
}
Ok(id)
}
}
pub fn reference(&self, id_str: &str) -> Result<IdRef<T>, IdError> {
match self.ids.get(id_str) {
Some(v) => Ok(IdRef::<T>(*v)),
None => Err(IdError::NonExistent)
}
}
}
impl<T: IdValue> Default for IdManager<T> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_id_ref_eq() {
let id = IdRef::<u32>(0);
let id_ref1 = IdRef::<u32>(0);
let id_ref2 = IdRef::<u32>(1);
assert_eq!(id_ref1, id);
assert_ne!(id_ref2, id);
assert_ne!(id_ref1, id_ref2)
}
#[test]
fn test_id_manager_create() {
let mut idman = IdManager::<u64>::new();
let id1 = idman.create("foo");
let id2 = idman.create("bar");
let id3 = idman.create("foo");
assert!(id1.is_ok());
assert!(id2.is_ok());
assert!(id3.is_err());
assert_ne!(id1.unwrap().0, id2.unwrap().0);
}
#[test]
fn test_id_manager_reference() {
let mut idman = IdManager::<u128>::new();
let id1 = idman.create("foo").unwrap();
let id2 = idman.create("bar").unwrap();
let idref1 = idman.reference("foo").unwrap();
let idref2 = idman.reference("bar").unwrap();
assert_eq!(idref1, id1);
assert_ne!(idref2, id1);
assert_eq!(idref2, id2);
assert_eq!(idref1, id1);
}
#[test]
fn test_id_hashing() {
let mut idman = IdManager::<u64>::new();
let id1 = idman.create("foo").unwrap();
let id2 = idman.create("bar").unwrap();
let h: HashMap<IdRef<u64>, String> = HashMap::from([
(id1, "id1".into()),
(id2, "id2".into())
]);
let idref1 = idman.reference("foo").unwrap();
let idref2 = idman.reference("bar").unwrap();
assert_eq!("id1", h.get(&idref1).unwrap());
assert_eq!("id2", h.get(&idref2).unwrap());
}
#[test]
fn test_id_create_overflow() {
let mut idman = IdManager::<u8>::new();
for i in 1..=256 {
idman.create(&i.to_string()).unwrap();
}
assert_eq!(IdError::MaximumNumberCreated, idman.create("257").unwrap_err());
}
}