use crate::{Clear, Len, Map, MapGet, MapInsert, MapMut};
use alloc::{boxed::Box, sync::Arc};
use core::{
any::{Any, TypeId},
marker::PhantomData,
};
#[derive(Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct AnyMap<T: ?Sized = dyn Any, M = DefaultBackingMap<AnyMapKey, Box<T>>> {
map: M,
marker: PhantomData<Arc<(AnyMapKey, Box<T>)>>,
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum AnyMapKey {
Id(usize),
TypeId(TypeId),
}
impl AnyMapKey {
#[inline]
pub fn with_type<T: 'static>() -> Self {
AnyMapKey::TypeId(TypeId::of::<T>())
}
#[inline]
pub fn is_type<T: 'static>(&self) -> bool {
match self {
AnyMapKey::TypeId(id) => *id == TypeId::of::<T>(),
_ => true,
}
}
}
#[cfg(feature = "std")]
type DefaultBackingMap<K, V> = std::collections::HashMap<K, V>;
#[cfg(not(feature = "std"))]
type DefaultBackingMap<K, V> = alloc::collections::BTreeMap<K, V>;
impl<T: ?Sized, M> AnyMap<T, M> {
#[inline]
pub fn new() -> Self
where
M: Default,
{
Self {
map: Default::default(),
marker: PhantomData,
}
}
#[inline]
pub fn len(&self) -> usize
where
M: Len,
{
self.map.len()
}
#[inline]
pub fn clear(&mut self)
where
M: Clear,
{
self.map.clear()
}
}
impl<T: ?Sized, M> AnyMap<T, M>
where
M: Map<Key = AnyMapKey, Value = Box<T>>,
M::Value: Downcast,
{
#[inline]
pub fn get<V: 'static>(&self) -> Option<&V>
where
M: MapGet<AnyMapKey>,
{
self.get_by_key(&AnyMapKey::with_type::<V>())
}
#[inline]
pub fn get_mut<V: 'static>(&mut self) -> Option<&mut V>
where
M: MapMut<AnyMapKey>,
{
self.get_by_key_mut(&AnyMapKey::with_type::<V>())
}
#[inline]
pub fn remove<V: 'static>(&mut self) -> Option<V>
where
M: MapMut<AnyMapKey>,
{
self.remove_by_key(&AnyMapKey::with_type::<V>())
}
#[inline]
pub fn insert<V: 'static>(&mut self, value: V) -> Option<V>
where
M: MapInsert,
V: IntoDowncast<Box<T>>,
{
self.insert_by_key(AnyMapKey::with_type::<V>(), value)
}
#[inline]
pub fn get_by_key<V: 'static>(&self, key: &AnyMapKey) -> Option<&V>
where
M: MapGet<AnyMapKey>,
{
self.map.get(key)?.downcast_as_ref()
}
#[inline]
pub fn get_by_key_mut<V: 'static>(&mut self, key: &AnyMapKey) -> Option<&mut V>
where
M: MapMut<AnyMapKey>,
{
self.map.get_mut(key)?.downcast_as_mut()
}
#[inline]
pub fn remove_by_key<V: 'static>(&mut self, key: &AnyMapKey) -> Option<V>
where
M: MapMut<AnyMapKey>,
{
self.map.remove(key)?.downcast_into()
}
#[inline]
pub fn insert_by_key<V: 'static>(&mut self, key: AnyMapKey, value: V) -> Option<V>
where
M: MapInsert,
V: IntoDowncast<Box<T>>,
{
if let AnyMapKey::TypeId(id) = key {
if id != TypeId::of::<V>() {
return None;
}
}
self.map.insert(key, value.into())?.downcast_into()
}
}
mod collections_impl {
use super::AnyMap;
use crate::{Clear, Len};
impl<T: ?Sized, M: Clear> Clear for AnyMap<T, M> {
#[inline]
fn clear(&mut self) {
self.clear();
}
}
impl<T: ?Sized, M: Len> Len for AnyMap<T, M> {
#[inline]
fn len(&self) -> usize {
self.len()
}
}
}
pub trait Downcast {
fn downcast_into<T: 'static>(self) -> Option<T>;
fn downcast_as_mut<T: 'static>(&mut self) -> Option<&mut T>;
fn downcast_as_ref<T: 'static>(&self) -> Option<&T>;
}
pub trait IntoDowncast<T: ?Sized + Downcast> {
fn into(self) -> T;
}
macro_rules! impl_box_downcast {
($any_trait:ident $(+ $auto_traits:ident)*) => {
impl Downcast for Box<dyn $any_trait $(+ $auto_traits)*> {
#[inline]
fn downcast_into<T: 'static>(self) -> Option<T> {
Some(*self.downcast().ok()?)
}
#[inline]
fn downcast_as_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.as_mut().downcast_mut()
}
#[inline]
fn downcast_as_ref<T: 'static>(&self) -> Option<&T> {
self.as_ref().downcast_ref()
}
}
impl<T: $any_trait $(+ $auto_traits)*> IntoDowncast<Box<dyn $any_trait $(+ $auto_traits)*>> for T {
#[inline]
fn into(self) -> Box<dyn $any_trait $(+ $auto_traits)*> {
Box::new(self)
}
}
}
}
impl_box_downcast!(Any);
impl_box_downcast!(Any + Send);
impl_box_downcast!(Any + Send + Sync);
#[cfg(test)]
mod tests {
use super::AnyMap;
use crate::{Clear, Len};
use core::any::Any;
#[test]
fn test_new_sync_send() {
let _map = <AnyMap>::new();
let _map = AnyMap::<dyn Any + Send>::new();
let _map = AnyMap::<dyn Any + Send + Sync>::new();
}
#[test]
fn test_clear_len() {
let mut map = <AnyMap>::new();
map.insert(1usize);
assert_eq!(Len::len(&map), 1);
Clear::clear(&mut map);
assert!(Len::is_empty(&map));
}
}