use std::any::{Any, TypeId};
use std::collections::HashMap;
pub trait Resource: Send + Sync + 'static {}
impl<T: Send + Sync + 'static> Resource for T {}
struct ResourceEntry {
data: Box<dyn Any + Send + Sync>,
type_name: &'static str,
}
#[derive(Default)]
pub struct Resources {
storage: HashMap<TypeId, ResourceEntry>,
}
impl Resources {
pub fn new() -> Self {
Self {
storage: HashMap::new(),
}
}
pub fn insert<R: Resource>(&mut self, resource: R) -> Option<R> {
let type_id = TypeId::of::<R>();
let type_name = std::any::type_name::<R>();
let entry = ResourceEntry {
data: Box::new(resource),
type_name,
};
self.storage
.insert(type_id, entry)
.and_then(|old| old.data.downcast::<R>().ok().map(|b| *b))
}
pub fn get<R: Resource>(&self) -> Option<&R> {
let type_id = TypeId::of::<R>();
self.storage
.get(&type_id)
.and_then(|entry| entry.data.downcast_ref())
}
pub fn get_mut<R: Resource>(&mut self) -> Option<&mut R> {
let type_id = TypeId::of::<R>();
self.storage
.get_mut(&type_id)
.and_then(|entry| entry.data.downcast_mut())
}
pub fn remove<R: Resource>(&mut self) -> Option<R> {
let type_id = TypeId::of::<R>();
self.storage
.remove(&type_id)
.and_then(|entry| entry.data.downcast::<R>().ok().map(|b| *b))
}
pub fn contains<R: Resource>(&self) -> bool {
let type_id = TypeId::of::<R>();
self.storage.contains_key(&type_id)
}
pub fn len(&self) -> usize {
self.storage.len()
}
pub fn is_empty(&self) -> bool {
self.storage.is_empty()
}
pub fn clear(&mut self) {
self.storage.clear();
}
pub fn get_or_insert_with<R: Resource>(&mut self, f: impl FnOnce() -> R) -> &mut R {
let type_id = TypeId::of::<R>();
if !self.storage.contains_key(&type_id) {
self.insert(f());
}
self.get_mut::<R>().unwrap()
}
pub fn get_or_default<R: Resource + Default>(&mut self) -> &mut R {
self.get_or_insert_with(R::default)
}
pub fn type_names(&self) -> impl Iterator<Item = &'static str> + '_ {
self.storage.values().map(|entry| entry.type_name)
}
}
impl std::fmt::Debug for Resources {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Resources")
.field("count", &self.storage.len())
.field("types", &self.type_names().collect::<Vec<_>>())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_insert_and_get() {
let mut resources = Resources::new();
resources.insert(42i32);
resources.insert("hello".to_string());
assert_eq!(*resources.get::<i32>().unwrap(), 42);
assert_eq!(resources.get::<String>().unwrap(), "hello");
}
#[test]
fn test_get_mut() {
let mut resources = Resources::new();
resources.insert(vec![1, 2, 3]);
resources.get_mut::<Vec<i32>>().unwrap().push(4);
assert_eq!(resources.get::<Vec<i32>>().unwrap(), &vec![1, 2, 3, 4]);
}
#[test]
fn test_replace() {
let mut resources = Resources::new();
resources.insert(10i32);
let old = resources.insert(20i32);
assert_eq!(old, Some(10));
assert_eq!(*resources.get::<i32>().unwrap(), 20);
}
#[test]
fn test_remove() {
let mut resources = Resources::new();
resources.insert(42i32);
let removed = resources.remove::<i32>();
assert_eq!(removed, Some(42));
assert!(resources.get::<i32>().is_none());
}
#[test]
fn test_contains() {
let mut resources = Resources::new();
assert!(!resources.contains::<i32>());
resources.insert(42i32);
assert!(resources.contains::<i32>());
}
#[test]
fn test_get_or_default() {
let mut resources = Resources::new();
let val = resources.get_or_default::<Vec<i32>>();
val.push(1);
assert_eq!(resources.get::<Vec<i32>>().unwrap(), &vec![1]);
}
#[test]
fn test_get_or_insert_with() {
let mut resources = Resources::new();
let mut called = false;
resources.get_or_insert_with(|| {
called = true;
42i32
});
assert!(called);
called = false;
resources.get_or_insert_with(|| {
called = true;
100i32
});
assert!(!called); assert_eq!(*resources.get::<i32>().unwrap(), 42);
}
}