use std::collections::HashMap;
use std::{error, fmt};
pub struct RefMap {
references: HashMap<String, u64>,
redirects: HashMap<String, String>,
}
impl RefMap {
pub fn new() -> Self {
RefMap {
references: HashMap::new(),
redirects: HashMap::new(),
}
}
pub fn add_ref(&mut self, id: String) -> u64 {
let ref_id = {
if let Some(ref_id) = self.redirects.get(&id) {
ref_id.clone()
} else {
id
}
};
if let Some(ref_count) = self.references.remove(&ref_id) {
let new_ref_count = ref_count + 1;
self.references.insert(ref_id, new_ref_count);
new_ref_count
} else {
self.references.insert(ref_id, 1);
1
}
}
pub fn update_ref(&mut self, old_id: String, new_id: String) -> Result<(), RefUpdateError> {
if let Some(ref_count) = self.references.remove(&old_id) {
self.references.insert(new_id.clone(), ref_count);
for (_, v) in self.redirects.iter_mut().filter(|(_, v)| **v == old_id) {
*v = new_id.clone()
}
self.redirects.insert(old_id, new_id);
Ok(())
} else {
Err(RefUpdateError { id: new_id })
}
}
pub fn remove_ref(&mut self, id: &str) -> Option<String> {
let ref_id = {
if !self.references.contains_key(id) {
if let Some(ref_id) = self.redirects.get(id) {
ref_id.to_string()
} else {
panic!("Trying to remove a reference that does not exist: {}", id)
}
} else {
id.to_string()
}
};
let ref_count = match self.references.remove(&ref_id) {
Some(ref_count) => ref_count,
None => panic!("Trying to remove a reference that does not exist: {}", id),
};
if ref_count == 1 {
self.references.remove(&ref_id);
self.redirects.retain(|_, target_id| target_id != id);
Some(ref_id)
} else {
self.references.insert(ref_id, ref_count - 1);
None
}
}
}
#[derive(Debug)]
pub struct RefUpdateError {
pub id: String,
}
impl error::Error for RefUpdateError {}
impl fmt::Display for RefUpdateError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Unable to update ref id for {}", self.id)
}
}
#[cfg(test)]
pub mod tests {
use super::*;
#[test]
fn test_add_ref() {
let mut ref_map = RefMap::new();
let ref_count = ref_map.add_ref("test_id".to_string());
assert_eq!(ref_count, 1);
let ref_count = ref_map.add_ref("test_id".to_string());
assert_eq!(ref_count, 2);
let ref_count = ref_map.add_ref("test_id_2".to_string());
assert_eq!(ref_count, 1);
}
#[test]
fn test_remove_ref() {
let mut ref_map = RefMap::new();
let ref_count = ref_map.add_ref("test_id".to_string());
assert_eq!(ref_count, 1);
let ref_count = ref_map.add_ref("test_id".to_string());
assert_eq!(ref_count, 2);
let id = ref_map.remove_ref("test_id");
assert_eq!(id, None);
assert_eq!(ref_map.references.get("test_id").cloned(), Some(1 as u64));
let id = ref_map.remove_ref("test_id");
assert_eq!(id, Some("test_id".to_string()));
assert_eq!(ref_map.references.get("test_id"), None);
}
#[test]
#[should_panic]
fn test_remove_ref_panic() {
let mut ref_map = RefMap::new();
ref_map.remove_ref("test_id");
}
#[test]
fn test_update_ref() {
let mut ref_map = RefMap::new();
let ref_count = ref_map.add_ref("old_id".to_string());
assert_eq!(ref_count, 1);
ref_map
.update_ref("old_id".to_string(), "new_id".to_string())
.expect("Unable to update reference");
let ref_count = ref_map.add_ref("new_id".to_string());
assert_eq!(ref_count, 2);
let id = ref_map.remove_ref("old_id");
assert_eq!(id, None);
let id = ref_map.remove_ref("new_id");
assert_eq!(id, Some("new_id".to_string()));
}
#[test]
#[should_panic]
fn test_update_ref_panic() {
let mut ref_map = RefMap::new();
let ref_count = ref_map.add_ref("old_id".to_string());
assert_eq!(ref_count, 1);
ref_map
.update_ref("old_id".to_string(), "new_id".to_string())
.expect("Unable to update reference");
let id = ref_map.remove_ref("new_id");
assert_eq!(id, Some("new_id".to_string()));
ref_map.remove_ref("old_id");
}
}