use std::any::{Any, TypeId};
use std::collections::{HashMap, HashSet};
use std::hash::Hash;
use std::sync::{mpsc, Arc, Mutex, RwLock};
use std::time::Instant;
use crate::event::ChangeEvent;
use crate::iter::ChangeIterator;
use crate::property::Property;
pub struct PropertyBag {
values: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
}
impl PropertyBag {
pub fn new() -> Self {
Self {
values: HashMap::new(),
}
}
pub fn get<P: Property>(&self) -> Option<P> {
let type_id = TypeId::of::<P>();
self.values
.get(&type_id)
.and_then(|boxed| boxed.downcast_ref::<P>())
.cloned()
}
pub fn set<P: Property>(&mut self, value: P) -> bool {
let type_id = TypeId::of::<P>();
let current = self
.values
.get(&type_id)
.and_then(|boxed| boxed.downcast_ref::<P>());
if current != Some(&value) {
self.values.insert(type_id, Box::new(value));
true
} else {
false
}
}
pub fn remove<P: Property>(&mut self) -> bool {
let type_id = TypeId::of::<P>();
self.values.remove(&type_id).is_some()
}
pub fn contains<P: Property>(&self) -> bool {
let type_id = TypeId::of::<P>();
self.values.contains_key(&type_id)
}
pub fn len(&self) -> usize {
self.values.len()
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
pub fn clear(&mut self) {
self.values.clear();
}
}
impl Default for PropertyBag {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for PropertyBag {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PropertyBag")
.field("property_count", &self.values.len())
.finish()
}
}
pub struct StateStore<Id>
where
Id: Clone + Eq + Hash + Send + Sync + 'static,
{
entities: Arc<RwLock<HashMap<Id, PropertyBag>>>,
watched: Arc<RwLock<HashSet<(Id, &'static str)>>>,
event_tx: mpsc::Sender<ChangeEvent<Id>>,
event_rx: Arc<Mutex<mpsc::Receiver<ChangeEvent<Id>>>>,
}
impl<Id> StateStore<Id>
where
Id: Clone + Eq + Hash + Send + Sync + 'static,
{
pub fn new() -> Self {
let (event_tx, event_rx) = mpsc::channel();
Self {
entities: Arc::new(RwLock::new(HashMap::new())),
watched: Arc::new(RwLock::new(HashSet::new())),
event_tx,
event_rx: Arc::new(Mutex::new(event_rx)),
}
}
pub fn get<P: Property>(&self, entity_id: &Id) -> Option<P> {
let entities = self.entities.read().ok()?;
entities.get(entity_id)?.get::<P>()
}
pub fn set<P: Property>(&self, entity_id: &Id, value: P) {
let changed = {
let mut entities = match self.entities.write() {
Ok(e) => e,
Err(_) => return,
};
let bag = entities
.entry(entity_id.clone())
.or_insert_with(PropertyBag::new);
bag.set(value)
};
if changed {
self.maybe_emit_change(entity_id, P::KEY);
}
}
pub fn watch(&self, entity_id: Id, property_key: &'static str) {
if let Ok(mut watched) = self.watched.write() {
watched.insert((entity_id, property_key));
}
}
pub fn unwatch(&self, entity_id: &Id, property_key: &'static str) {
if let Ok(mut watched) = self.watched.write() {
watched.remove(&(entity_id.clone(), property_key));
}
}
pub fn is_watched(&self, entity_id: &Id, property_key: &'static str) -> bool {
self.watched
.read()
.map(|w| w.contains(&(entity_id.clone(), property_key)))
.unwrap_or(false)
}
pub fn iter(&self) -> ChangeIterator<Id> {
ChangeIterator::new(Arc::clone(&self.event_rx))
}
pub fn entity_count(&self) -> usize {
self.entities.read().map(|e| e.len()).unwrap_or(0)
}
pub fn is_empty(&self) -> bool {
self.entity_count() == 0
}
pub fn entity_ids(&self) -> Vec<Id> {
self.entities
.read()
.map(|e| e.keys().cloned().collect())
.unwrap_or_default()
}
pub fn remove_entity(&self, entity_id: &Id) -> bool {
self.entities
.write()
.map(|mut e| e.remove(entity_id).is_some())
.unwrap_or(false)
}
pub fn clear(&self) {
if let Ok(mut entities) = self.entities.write() {
entities.clear();
}
if let Ok(mut watched) = self.watched.write() {
watched.clear();
}
}
pub fn event_sender(&self) -> mpsc::Sender<ChangeEvent<Id>> {
self.event_tx.clone()
}
fn maybe_emit_change(&self, entity_id: &Id, property_key: &'static str) {
let is_watched = self
.watched
.read()
.map(|w| w.contains(&(entity_id.clone(), property_key)))
.unwrap_or(false);
if is_watched {
let event = ChangeEvent {
entity_id: entity_id.clone(),
property_key,
timestamp: Instant::now(),
};
let _ = self.event_tx.send(event);
}
}
}
impl<Id> Default for StateStore<Id>
where
Id: Clone + Eq + Hash + Send + Sync + 'static,
{
fn default() -> Self {
Self::new()
}
}
impl<Id> Clone for StateStore<Id>
where
Id: Clone + Eq + Hash + Send + Sync + 'static,
{
fn clone(&self) -> Self {
Self {
entities: Arc::clone(&self.entities),
watched: Arc::clone(&self.watched),
event_tx: self.event_tx.clone(),
event_rx: Arc::clone(&self.event_rx),
}
}
}
impl<Id> std::fmt::Debug for StateStore<Id>
where
Id: Clone + Eq + Hash + Send + Sync + std::fmt::Debug + 'static,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("StateStore")
.field("entity_count", &self.entity_count())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, PartialEq, Debug)]
struct TestProp(i32);
impl Property for TestProp {
const KEY: &'static str = "test";
}
#[derive(Clone, PartialEq, Debug)]
struct OtherProp(String);
impl Property for OtherProp {
const KEY: &'static str = "other";
}
#[test]
fn test_property_bag_basic() {
let mut bag = PropertyBag::new();
assert!(bag.is_empty());
assert!(bag.get::<TestProp>().is_none());
assert!(bag.set(TestProp(42)));
assert!(!bag.is_empty());
assert_eq!(bag.get::<TestProp>(), Some(TestProp(42)));
assert!(!bag.set(TestProp(42)));
assert!(bag.set(TestProp(99)));
assert_eq!(bag.get::<TestProp>(), Some(TestProp(99)));
}
#[test]
fn test_property_bag_multiple_types() {
let mut bag = PropertyBag::new();
bag.set(TestProp(42));
bag.set(OtherProp("hello".to_string()));
assert_eq!(bag.len(), 2);
assert_eq!(bag.get::<TestProp>(), Some(TestProp(42)));
assert_eq!(bag.get::<OtherProp>(), Some(OtherProp("hello".to_string())));
}
#[test]
fn test_state_store_basic() {
let store = StateStore::<String>::new();
assert!(store.is_empty());
assert!(store.get::<TestProp>(&"entity-1".to_string()).is_none());
store.set(&"entity-1".to_string(), TestProp(42));
assert_eq!(store.entity_count(), 1);
assert_eq!(
store.get::<TestProp>(&"entity-1".to_string()),
Some(TestProp(42))
);
}
#[test]
fn test_state_store_watch() {
let store = StateStore::<String>::new();
let entity_id = "entity-1".to_string();
assert!(!store.is_watched(&entity_id, TestProp::KEY));
store.watch(entity_id.clone(), TestProp::KEY);
assert!(store.is_watched(&entity_id, TestProp::KEY));
store.unwatch(&entity_id, TestProp::KEY);
assert!(!store.is_watched(&entity_id, TestProp::KEY));
}
#[test]
fn test_state_store_change_event() {
let store = StateStore::<String>::new();
let entity_id = "entity-1".to_string();
store.watch(entity_id.clone(), TestProp::KEY);
store.set(&entity_id, TestProp(42));
let iter = store.iter();
let event = iter.recv_timeout(std::time::Duration::from_millis(100));
assert!(event.is_some());
let event = event.unwrap();
assert_eq!(event.entity_id, entity_id);
assert_eq!(event.property_key, TestProp::KEY);
}
#[test]
fn test_state_store_no_event_when_not_watched() {
let store = StateStore::<String>::new();
let entity_id = "entity-1".to_string();
store.set(&entity_id, TestProp(42));
let iter = store.iter();
let event = iter.recv_timeout(std::time::Duration::from_millis(50));
assert!(event.is_none());
}
#[test]
fn test_state_store_no_event_when_same_value() {
let store = StateStore::<String>::new();
let entity_id = "entity-1".to_string();
store.watch(entity_id.clone(), TestProp::KEY);
store.set(&entity_id, TestProp(42));
let iter = store.iter();
let event = iter.recv_timeout(std::time::Duration::from_millis(100));
assert!(event.is_some());
store.set(&entity_id, TestProp(42));
let event = iter.recv_timeout(std::time::Duration::from_millis(50));
assert!(event.is_none());
}
#[test]
fn test_state_store_clone() {
let store = StateStore::<String>::new();
let cloned = store.clone();
store.set(&"entity-1".to_string(), TestProp(42));
assert_eq!(
cloned.get::<TestProp>(&"entity-1".to_string()),
Some(TestProp(42))
);
}
}