use std::{
borrow::Borrow,
collections::HashMap,
fmt,
hash::Hash,
ops::{Deref, DerefMut},
};
use rt_ref::{BorrowFail, Cell, Ref, RefMut};
use crate::Entry;
#[derive(Debug)]
pub struct RtMap<K, V>(HashMap<K, Cell<V>>);
impl<K, V> Default for RtMap<K, V> {
fn default() -> Self {
Self(Default::default())
}
}
macro_rules! borrow_panic {
($key:ident) => {
panic!(
"Expected to borrow `{key:?}`, but it does not exist.",
key = $key
)
};
}
impl<K, V> RtMap<K, V>
where
K: Hash + Eq,
{
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(capacity: usize) -> Self {
Self(HashMap::with_capacity(capacity))
}
pub fn capacity(&self) -> usize {
self.0.capacity()
}
pub fn into_inner(self) -> HashMap<K, Cell<V>> {
self.0
}
pub fn entry(&mut self, k: K) -> Entry<'_, K, V> {
Entry::new(self.0.entry(k))
}
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
self.0.insert(k, Cell::new(v)).map(Cell::into_inner)
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn remove<Q>(&mut self, k: &Q) -> Option<V>
where
Q: ?Sized + Hash + Eq,
K: Borrow<Q>,
{
self.0.remove(k).map(Cell::into_inner)
}
pub fn contains_key<Q>(&self, k: &Q) -> bool
where
Q: ?Sized + Hash + Eq,
K: Borrow<Q>,
{
self.0.contains_key(k)
}
pub fn borrow<Q>(&self, k: &Q) -> Ref<V>
where
Q: ?Sized + Hash + Eq + fmt::Debug,
K: Borrow<Q>,
{
self.0
.get(k)
.map(|cell| Ref::new(cell.borrow()))
.unwrap_or_else(|| borrow_panic!(k))
}
pub fn try_borrow<Q>(&self, k: &Q) -> Result<Ref<V>, BorrowFail>
where
Q: ?Sized + Hash + Eq,
K: Borrow<Q>,
{
self.0
.get(k)
.ok_or(BorrowFail::ValueNotFound)
.and_then(|cell| cell.try_borrow().map(Ref::new))
}
pub fn borrow_mut<Q>(&self, k: &Q) -> RefMut<V>
where
Q: ?Sized + Hash + Eq + fmt::Debug,
K: Borrow<Q>,
{
self.0
.get(k)
.map(|cell| RefMut::new(cell.borrow_mut()))
.unwrap_or_else(|| borrow_panic!(k))
}
pub fn try_borrow_mut<Q>(&self, k: &Q) -> Result<RefMut<V>, BorrowFail>
where
Q: ?Sized + Hash + Eq,
K: Borrow<Q>,
{
self.0
.get(k)
.ok_or(BorrowFail::ValueNotFound)
.and_then(|r_cell| r_cell.try_borrow_mut().map(RefMut::new))
}
pub fn get_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
where
Q: ?Sized + Hash + Eq,
K: Borrow<Q>,
{
self.get_resource_mut(k)
}
pub fn get_resource_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
where
Q: ?Sized + Hash + Eq,
K: Borrow<Q>,
{
self.0.get_mut(k).map(Cell::get_mut)
}
pub fn get_raw<Q>(&self, k: &Q) -> Option<&Cell<V>>
where
Q: ?Sized + Hash + Eq,
K: Borrow<Q>,
{
self.0.get(k)
}
}
impl<K, V> Deref for RtMap<K, V> {
type Target = HashMap<K, Cell<V>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<K, V> DerefMut for RtMap<K, V> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[cfg(test)]
mod tests {
use rt_ref::BorrowFail;
use super::RtMap;
#[derive(Debug, Default, PartialEq)]
struct Res;
#[derive(Debug, Default, PartialEq)]
struct Value(u32);
#[test]
fn insert() {
let mut rt_map = RtMap::new();
rt_map.insert('a', Res);
assert!(rt_map.contains_key(&'a'));
assert!(!rt_map.contains_key(&'b'));
}
#[test]
fn with_capacity_reserves_enough_capacity() {
let map: RtMap<i32, i32> = RtMap::with_capacity(100);
assert!(map.capacity() >= 100);
}
#[test]
fn into_inner() {
let mut rt_map = RtMap::new();
rt_map.insert('a', Res);
let inner_map = rt_map.into_inner();
assert!(inner_map.contains_key(&'a'));
}
#[test]
fn deref_and_deref_mut() {
let mut rt_map = RtMap::new();
rt_map.insert('a', 0);
rt_map.insert('b', 1);
rt_map.iter_mut().for_each(|(_k, v)| *v.borrow_mut() += 1);
let a = rt_map.remove(&'a');
assert_eq!(Some(1), a);
let b = rt_map.iter().next();
assert_eq!(Some(2), b.map(|(_k, v)| *v.borrow()));
}
#[test]
fn is_empty_returns_true_when_map_does_not_contain_items() {
let rt_map = RtMap::<char, u32>::new();
assert!(rt_map.is_empty());
}
#[test]
fn is_empty_returns_false_when_map_contains_items() {
let mut rt_map = RtMap::new();
rt_map.insert('a', 0);
assert!(!rt_map.is_empty());
}
#[test]
fn entry_insert_value() {
let mut rt_map = RtMap::new();
let ref_mut = rt_map.entry('a').or_insert(1);
assert_eq!(1, *ref_mut);
drop(ref_mut);
let ref_mut = rt_map.entry('a').or_insert(2);
assert_eq!(1, *ref_mut);
drop(ref_mut);
rt_map.remove(&'a');
let ref_mut = rt_map.entry('a').or_insert_with(|| 3);
assert_eq!(3, *ref_mut);
drop(ref_mut);
}
#[test]
fn get_mut_returns_mutable_reference_to_value() {
let mut rt_map = RtMap::new();
rt_map.insert('a', Value(1));
let value = rt_map.get_mut(&'a');
assert!(value.is_some());
if let Some(value) = value {
*value = Value(2);
}
let value = rt_map.get_mut(&'a').map(|value| value.0);
assert_eq!(Some(2), value);
}
#[test]
#[should_panic(expected = "but it was already borrowed")]
fn read_write_fails() {
let mut rt_map = RtMap::new();
rt_map.insert('a', Res);
let _read = rt_map.borrow(&'a');
let _write = rt_map.borrow_mut(&'a');
}
#[test]
#[should_panic(expected = "but it was already borrowed mutably")]
fn write_read_fails() {
let mut rt_map = RtMap::new();
rt_map.insert('a', Res);
let _write = rt_map.borrow_mut(&'a');
let _read = rt_map.borrow(&'a');
}
#[test]
fn remove_insert() {
let mut rt_map = RtMap::new();
rt_map.insert('a', Res);
assert!(rt_map.contains_key(&'a'));
rt_map.remove(&'a').unwrap();
assert!(!rt_map.contains_key(&'a'));
rt_map.insert('a', Res);
assert!(rt_map.contains_key(&'a'));
}
#[test]
#[should_panic(expected = "Expected to borrow `'a'`, but it does not exist.")]
fn borrow_before_insert_panics() {
let rt_map = RtMap::<char, i32>::new();
rt_map.borrow(&'a');
}
#[test]
#[should_panic(expected = "Expected to borrow `'a'`, but it does not exist.")]
fn borrow_mut_before_insert_panics() {
let rt_map = RtMap::<char, i32>::new();
rt_map.borrow_mut(&'a');
}
#[test]
fn borrow_mut_try_borrow_returns_borrow_conflict_imm() {
let mut rt_map = RtMap::new();
rt_map.insert('a', Res);
let _res = rt_map.borrow_mut(&'a');
assert_eq!(Err(BorrowFail::BorrowConflictImm), rt_map.try_borrow(&'a'));
}
#[test]
fn borrow_try_borrow_mut_returns_borrow_conflict_mut() {
let mut rt_map = RtMap::new();
rt_map.insert('a', Res);
let _res = rt_map.borrow(&'a');
assert_eq!(
Err(BorrowFail::BorrowConflictMut),
rt_map.try_borrow_mut(&'a')
);
}
#[test]
fn borrow_mut_borrow_mut_returns_borrow_conflict_mut() {
let mut rt_map = RtMap::new();
rt_map.insert('a', Res);
let _res = rt_map.borrow_mut(&'a');
assert_eq!(
Err(BorrowFail::BorrowConflictMut),
rt_map.try_borrow_mut(&'a')
);
}
#[test]
fn try_borrow_before_insert_returns_value_not_found() {
let rt_map = RtMap::<char, Res>::new();
assert_eq!(Err(BorrowFail::ValueNotFound), rt_map.try_borrow(&'a'));
}
#[test]
fn try_borrow_mut_before_insert_returns_value_not_found() {
let rt_map = RtMap::<char, Res>::new();
assert_eq!(Err(BorrowFail::ValueNotFound), rt_map.try_borrow_mut(&'a'));
}
#[test]
#[should_panic(expected = "Expected to borrow `'a'`, but it does not exist.")]
fn borrow_before_insert_panics_value_not_found() {
let rt_map = RtMap::<char, Res>::new();
rt_map.borrow(&'a');
}
#[test]
#[should_panic(expected = "Expected to borrow `'a'`, but it does not exist.")]
fn borrow_mut_before_insert_panics_value_not_found() {
let rt_map = RtMap::<char, Res>::new();
rt_map.borrow_mut(&'a');
}
}