use parking_lot::RwLock;
use std::{any::TypeId, collections::hash_map, marker::PhantomData, ops::DerefMut};
use crate::{
map::Resource,
refs::{Ref, RefMut},
};
pub enum Entry<'a, T: Resource> {
Occupied(OccupiedEntry<'a, T>),
Vacant(VacantEntry<'a, T>),
}
pub struct OccupiedEntry<'a, T: Resource> {
base: hash_map::OccupiedEntry<'a, TypeId, RwLock<Box<dyn Resource>>>,
phantom_data: PhantomData<T>,
}
pub struct VacantEntry<'a, T: Resource> {
base: hash_map::VacantEntry<'a, TypeId, RwLock<Box<dyn Resource>>>,
phantom_data: PhantomData<T>,
}
impl<'a, T: Resource> Entry<'a, T> {
pub(crate) fn from_hash_map_entry(
entry: hash_map::Entry<'a, TypeId, RwLock<Box<dyn Resource>>>,
) -> Self {
match entry {
hash_map::Entry::Occupied(base) => Entry::Occupied(OccupiedEntry {
base,
phantom_data: PhantomData,
}),
hash_map::Entry::Vacant(base) => Entry::Vacant(VacantEntry {
base,
phantom_data: PhantomData,
}),
}
}
pub fn or_insert(self, default: T) -> RefMut<'a, T> {
self.or_insert_with(|| default)
}
pub fn or_insert_with(self, default: impl FnOnce() -> T) -> RefMut<'a, T> {
use Entry::*;
match self {
Occupied(occupied) => occupied.into_mut(),
Vacant(vacant) => vacant.insert(default()),
}
}
pub fn and_modify(mut self, f: impl FnOnce(&mut T)) -> Self {
if let Entry::Occupied(occupied) = &mut self {
f(occupied.get_mut().deref_mut());
}
self
}
}
impl<'a, T: Resource + Default> Entry<'a, T> {
pub fn or_default(self) -> RefMut<'a, T> {
self.or_insert_with(T::default)
}
}
impl<'a, T: Resource> OccupiedEntry<'a, T> {
pub fn get(&self) -> Ref<T> {
Ref::from_lock(self.base.get()).expect("entry API assumes unique access")
}
pub fn get_mut(&mut self) -> RefMut<T> {
RefMut::from_lock(self.base.get_mut()).expect("entry API assumes unique access")
}
pub fn into_mut(self) -> RefMut<'a, T> {
RefMut::from_lock(self.base.into_mut()).expect("entry API assumes unique access")
}
pub fn insert(&mut self, value: T) -> T {
*self
.base
.insert(RwLock::new(Box::new(value)))
.into_inner()
.downcast()
.unwrap_or_else(|_| panic!("downcasting resources should always succeed"))
}
pub fn remove(self) -> T {
*self
.base
.remove()
.into_inner()
.downcast()
.unwrap_or_else(|_| panic!("downcasting resources should always succeed"))
}
}
impl<'a, T: Resource> VacantEntry<'a, T> {
pub fn insert(self, value: T) -> RefMut<'a, T> {
RefMut::from_lock(self.base.insert(RwLock::new(Box::new(value))))
.expect("entry API assumes unique access")
}
}