1use std::any::{Any, TypeId};
8use std::collections::{HashMap, HashSet};
9use std::hash::Hash;
10use std::sync::{mpsc, Arc, Mutex, RwLock};
11use std::time::Instant;
12
13use crate::event::ChangeEvent;
14use crate::iter::ChangeIterator;
15use crate::property::Property;
16
17pub struct PropertyBag {
52 values: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
53}
54
55impl PropertyBag {
56 pub fn new() -> Self {
58 Self {
59 values: HashMap::new(),
60 }
61 }
62
63 pub fn get<P: Property>(&self) -> Option<P> {
67 let type_id = TypeId::of::<P>();
68 self.values
69 .get(&type_id)
70 .and_then(|boxed| boxed.downcast_ref::<P>())
71 .cloned()
72 }
73
74 pub fn set<P: Property>(&mut self, value: P) -> bool {
80 let type_id = TypeId::of::<P>();
81 let current = self
82 .values
83 .get(&type_id)
84 .and_then(|boxed| boxed.downcast_ref::<P>());
85
86 if current != Some(&value) {
87 self.values.insert(type_id, Box::new(value));
88 true
89 } else {
90 false
91 }
92 }
93
94 pub fn remove<P: Property>(&mut self) -> bool {
96 let type_id = TypeId::of::<P>();
97 self.values.remove(&type_id).is_some()
98 }
99
100 pub fn contains<P: Property>(&self) -> bool {
102 let type_id = TypeId::of::<P>();
103 self.values.contains_key(&type_id)
104 }
105
106 pub fn len(&self) -> usize {
108 self.values.len()
109 }
110
111 pub fn is_empty(&self) -> bool {
113 self.values.is_empty()
114 }
115
116 pub fn clear(&mut self) {
118 self.values.clear();
119 }
120}
121
122impl Default for PropertyBag {
123 fn default() -> Self {
124 Self::new()
125 }
126}
127
128impl std::fmt::Debug for PropertyBag {
129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130 f.debug_struct("PropertyBag")
131 .field("property_count", &self.values.len())
132 .finish()
133 }
134}
135
136pub struct StateStore<Id>
177where
178 Id: Clone + Eq + Hash + Send + Sync + 'static,
179{
180 entities: Arc<RwLock<HashMap<Id, PropertyBag>>>,
182
183 watched: Arc<RwLock<HashSet<(Id, &'static str)>>>,
185
186 event_tx: mpsc::Sender<ChangeEvent<Id>>,
188
189 event_rx: Arc<Mutex<mpsc::Receiver<ChangeEvent<Id>>>>,
191}
192
193impl<Id> StateStore<Id>
194where
195 Id: Clone + Eq + Hash + Send + Sync + 'static,
196{
197 pub fn new() -> Self {
199 let (event_tx, event_rx) = mpsc::channel();
200
201 Self {
202 entities: Arc::new(RwLock::new(HashMap::new())),
203 watched: Arc::new(RwLock::new(HashSet::new())),
204 event_tx,
205 event_rx: Arc::new(Mutex::new(event_rx)),
206 }
207 }
208
209 pub fn get<P: Property>(&self, entity_id: &Id) -> Option<P> {
213 let entities = self.entities.read().ok()?;
214 entities.get(entity_id)?.get::<P>()
215 }
216
217 pub fn set<P: Property>(&self, entity_id: &Id, value: P) {
222 let changed = {
223 let mut entities = match self.entities.write() {
224 Ok(e) => e,
225 Err(_) => return,
226 };
227 let bag = entities
228 .entry(entity_id.clone())
229 .or_insert_with(PropertyBag::new);
230 bag.set(value)
231 };
232
233 if changed {
234 self.maybe_emit_change(entity_id, P::KEY);
235 }
236 }
237
238 pub fn watch(&self, entity_id: Id, property_key: &'static str) {
242 if let Ok(mut watched) = self.watched.write() {
243 watched.insert((entity_id, property_key));
244 }
245 }
246
247 pub fn unwatch(&self, entity_id: &Id, property_key: &'static str) {
249 if let Ok(mut watched) = self.watched.write() {
250 watched.remove(&(entity_id.clone(), property_key));
251 }
252 }
253
254 pub fn is_watched(&self, entity_id: &Id, property_key: &'static str) -> bool {
256 self.watched
257 .read()
258 .map(|w| w.contains(&(entity_id.clone(), property_key)))
259 .unwrap_or(false)
260 }
261
262 pub fn iter(&self) -> ChangeIterator<Id> {
266 ChangeIterator::new(Arc::clone(&self.event_rx))
267 }
268
269 pub fn entity_count(&self) -> usize {
271 self.entities.read().map(|e| e.len()).unwrap_or(0)
272 }
273
274 pub fn is_empty(&self) -> bool {
276 self.entity_count() == 0
277 }
278
279 pub fn entity_ids(&self) -> Vec<Id> {
281 self.entities
282 .read()
283 .map(|e| e.keys().cloned().collect())
284 .unwrap_or_default()
285 }
286
287 pub fn remove_entity(&self, entity_id: &Id) -> bool {
289 self.entities
290 .write()
291 .map(|mut e| e.remove(entity_id).is_some())
292 .unwrap_or(false)
293 }
294
295 pub fn clear(&self) {
297 if let Ok(mut entities) = self.entities.write() {
298 entities.clear();
299 }
300 if let Ok(mut watched) = self.watched.write() {
301 watched.clear();
302 }
303 }
304
305 pub fn event_sender(&self) -> mpsc::Sender<ChangeEvent<Id>> {
310 self.event_tx.clone()
311 }
312
313 fn maybe_emit_change(&self, entity_id: &Id, property_key: &'static str) {
315 let is_watched = self
316 .watched
317 .read()
318 .map(|w| w.contains(&(entity_id.clone(), property_key)))
319 .unwrap_or(false);
320
321 if is_watched {
322 let event = ChangeEvent {
323 entity_id: entity_id.clone(),
324 property_key,
325 timestamp: Instant::now(),
326 };
327 let _ = self.event_tx.send(event);
328 }
329 }
330}
331
332impl<Id> Default for StateStore<Id>
333where
334 Id: Clone + Eq + Hash + Send + Sync + 'static,
335{
336 fn default() -> Self {
337 Self::new()
338 }
339}
340
341impl<Id> Clone for StateStore<Id>
342where
343 Id: Clone + Eq + Hash + Send + Sync + 'static,
344{
345 fn clone(&self) -> Self {
346 Self {
347 entities: Arc::clone(&self.entities),
348 watched: Arc::clone(&self.watched),
349 event_tx: self.event_tx.clone(),
350 event_rx: Arc::clone(&self.event_rx),
351 }
352 }
353}
354
355impl<Id> std::fmt::Debug for StateStore<Id>
356where
357 Id: Clone + Eq + Hash + Send + Sync + std::fmt::Debug + 'static,
358{
359 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
360 f.debug_struct("StateStore")
361 .field("entity_count", &self.entity_count())
362 .finish()
363 }
364}
365
366#[cfg(test)]
367mod tests {
368 use super::*;
369
370 #[derive(Clone, PartialEq, Debug)]
371 struct TestProp(i32);
372
373 impl Property for TestProp {
374 const KEY: &'static str = "test";
375 }
376
377 #[derive(Clone, PartialEq, Debug)]
378 struct OtherProp(String);
379
380 impl Property for OtherProp {
381 const KEY: &'static str = "other";
382 }
383
384 #[test]
385 fn test_property_bag_basic() {
386 let mut bag = PropertyBag::new();
387
388 assert!(bag.is_empty());
390 assert!(bag.get::<TestProp>().is_none());
391
392 assert!(bag.set(TestProp(42)));
394 assert!(!bag.is_empty());
395 assert_eq!(bag.get::<TestProp>(), Some(TestProp(42)));
396
397 assert!(!bag.set(TestProp(42)));
399
400 assert!(bag.set(TestProp(99)));
402 assert_eq!(bag.get::<TestProp>(), Some(TestProp(99)));
403 }
404
405 #[test]
406 fn test_property_bag_multiple_types() {
407 let mut bag = PropertyBag::new();
408
409 bag.set(TestProp(42));
410 bag.set(OtherProp("hello".to_string()));
411
412 assert_eq!(bag.len(), 2);
413 assert_eq!(bag.get::<TestProp>(), Some(TestProp(42)));
414 assert_eq!(bag.get::<OtherProp>(), Some(OtherProp("hello".to_string())));
415 }
416
417 #[test]
418 fn test_state_store_basic() {
419 let store = StateStore::<String>::new();
420
421 assert!(store.is_empty());
423 assert!(store.get::<TestProp>(&"entity-1".to_string()).is_none());
424
425 store.set(&"entity-1".to_string(), TestProp(42));
427 assert_eq!(store.entity_count(), 1);
428 assert_eq!(
429 store.get::<TestProp>(&"entity-1".to_string()),
430 Some(TestProp(42))
431 );
432 }
433
434 #[test]
435 fn test_state_store_watch() {
436 let store = StateStore::<String>::new();
437 let entity_id = "entity-1".to_string();
438
439 assert!(!store.is_watched(&entity_id, TestProp::KEY));
441
442 store.watch(entity_id.clone(), TestProp::KEY);
444 assert!(store.is_watched(&entity_id, TestProp::KEY));
445
446 store.unwatch(&entity_id, TestProp::KEY);
448 assert!(!store.is_watched(&entity_id, TestProp::KEY));
449 }
450
451 #[test]
452 fn test_state_store_change_event() {
453 let store = StateStore::<String>::new();
454 let entity_id = "entity-1".to_string();
455
456 store.watch(entity_id.clone(), TestProp::KEY);
458
459 store.set(&entity_id, TestProp(42));
461
462 let iter = store.iter();
464 let event = iter.recv_timeout(std::time::Duration::from_millis(100));
465 assert!(event.is_some());
466
467 let event = event.unwrap();
468 assert_eq!(event.entity_id, entity_id);
469 assert_eq!(event.property_key, TestProp::KEY);
470 }
471
472 #[test]
473 fn test_state_store_no_event_when_not_watched() {
474 let store = StateStore::<String>::new();
475 let entity_id = "entity-1".to_string();
476
477 store.set(&entity_id, TestProp(42));
479
480 let iter = store.iter();
482 let event = iter.recv_timeout(std::time::Duration::from_millis(50));
483 assert!(event.is_none());
484 }
485
486 #[test]
487 fn test_state_store_no_event_when_same_value() {
488 let store = StateStore::<String>::new();
489 let entity_id = "entity-1".to_string();
490
491 store.watch(entity_id.clone(), TestProp::KEY);
492
493 store.set(&entity_id, TestProp(42));
495
496 let iter = store.iter();
497 let event = iter.recv_timeout(std::time::Duration::from_millis(100));
498 assert!(event.is_some());
499
500 store.set(&entity_id, TestProp(42));
502 let event = iter.recv_timeout(std::time::Duration::from_millis(50));
503 assert!(event.is_none());
504 }
505
506 #[test]
507 fn test_state_store_clone() {
508 let store = StateStore::<String>::new();
509 let cloned = store.clone();
510
511 store.set(&"entity-1".to_string(), TestProp(42));
513 assert_eq!(
514 cloned.get::<TestProp>(&"entity-1".to_string()),
515 Some(TestProp(42))
516 );
517 }
518}