use core::any::{Any, TypeId};
use std::collections::{hash_map::Entry, HashMap};
use crate::nut::IMPOSSIBLE_ERR_MSG;
#[derive(Default)]
pub struct DomainState {
objects: Vec<Box<dyn Any>>,
index_map: HashMap<TypeId, usize>,
}
impl DomainState {
pub fn store<T: Any>(&mut self, obj: T) {
let id = TypeId::of::<T>();
match self.index_map.entry(id) {
Entry::Occupied(entry) => {
*self.objects[*entry.get()]
.downcast_mut()
.expect(IMPOSSIBLE_ERR_MSG) = obj;
}
Entry::Vacant(entry) => {
entry.insert(self.objects.len());
self.objects.push(Box::new(obj));
}
}
}
pub(crate) fn store_unchecked(&mut self, id: TypeId, obj: Box<dyn Any>) {
match self.index_map.entry(id) {
Entry::Occupied(entry) => {
self.objects[*entry.get()] = obj;
}
Entry::Vacant(entry) => {
entry.insert(self.objects.len());
self.objects.push(obj);
}
}
}
#[allow(clippy::unwrap_used)]
pub fn try_get<T: Any>(&self) -> Option<&T> {
self.index_map
.get(&TypeId::of::<T>())
.map(|index| self.objects[*index].as_ref().downcast_ref().unwrap())
}
#[allow(clippy::unwrap_used)]
pub fn try_get_mut<T: Any>(&mut self) -> Option<&mut T> {
if let Some(index) = self.index_map.get(&TypeId::of::<T>()) {
Some(self.objects[*index].as_mut().downcast_mut().unwrap())
} else {
None
}
}
#[allow(clippy::unwrap_used)]
pub fn try_get_2_mut<T1: Any, T2: Any>(&mut self) -> (Option<&mut T1>, Option<&mut T2>) {
let type_1: TypeId = TypeId::of::<T1>();
let type_2: TypeId = TypeId::of::<T2>();
assert_ne(type_1, type_2);
let i1 = self.index_map.get(&type_1);
let i2 = self.index_map.get(&type_2);
if i1.is_none() {
return (None, self.try_get_mut());
}
if i2.is_none() {
return (self.try_get_mut(), None);
}
let i1 = i1.unwrap();
let i2 = i2.unwrap();
let split = i1.min(i2) + 1;
let (left, right) = self.objects.split_at_mut(split);
let (t1, t2) = if i1 < i2 {
(&mut left[*i1], &mut right[i2 - split])
} else {
(&mut right[i1 - split], &mut left[*i2])
};
(
Some(t1.as_mut().downcast_mut().unwrap()),
Some(t2.as_mut().downcast_mut().unwrap()),
)
}
#[allow(clippy::unwrap_used)]
pub fn get<T: Any>(&self) -> &T {
self.try_get().expect("Not in domain")
}
#[allow(clippy::unwrap_used)]
pub fn get_mut<T: Any>(&mut self) -> &mut T {
self.try_get_mut().expect("Not in domain")
}
}
fn assert_ne(t1: TypeId, t2: TypeId) {
if t1 == t2 {
panic!("Cannot get two mutable references of same type from domain")
}
}