Skip to main content

concinnity_core/ecs/
event_store.rs

1// Owner of every `Events<E>` queue, keyed by event type. Queues are created
2// lazily on first mutable access and the store can rotate all of them at once,
3// so a frame driver never maintains a per-type rotation list (a queue missing
4// from such a list would buffer its events forever).
5
6use alloc::boxed::Box;
7use alloc::collections::BTreeMap;
8use core::any::{Any, TypeId};
9
10use crate::ecs::event::Events;
11
12// Object-safe view of one queue: rotation without knowing the event type,
13// plus downcast access back to the concrete `Events<E>`. `Send` so the store
14// (inside the world) can move to a simulation thread.
15trait AnyEventQueue: Send {
16    fn update(&mut self);
17    fn as_any(&self) -> &dyn Any;
18    fn as_any_mut(&mut self) -> &mut dyn Any;
19}
20
21impl<E: Send + 'static> AnyEventQueue for Events<E> {
22    fn update(&mut self) {
23        Events::update(self);
24    }
25    fn as_any(&self) -> &dyn Any {
26        self
27    }
28    fn as_any_mut(&mut self) -> &mut dyn Any {
29        self
30    }
31}
32
33#[derive(Default)]
34/// Type-keyed event queues, one per event type in use.
35pub struct EventStore {
36    queues: BTreeMap<TypeId, Box<dyn AnyEventQueue>>,
37}
38
39impl EventStore {
40    /// An empty store.
41    pub fn new() -> EventStore {
42        EventStore::default()
43    }
44
45    /// Borrow the queue for event type E, if one has been created.
46    pub fn get<E: 'static>(&self) -> Option<&Events<E>> {
47        self.queues
48            .get(&TypeId::of::<E>())
49            .and_then(|queue| queue.as_any().downcast_ref::<Events<E>>())
50    }
51
52    /// Mutably borrow the queue for event type E, creating an empty one on
53    /// first access so writers and readers never miss it.
54    pub fn get_mut_or_create<E: Send + 'static>(&mut self) -> &mut Events<E> {
55        self.queues
56            .entry(TypeId::of::<E>())
57            .or_insert_with(|| Box::new(Events::<E>::new()))
58            .as_any_mut()
59            .downcast_mut::<Events<E>>()
60            .expect("queue stored under E's TypeId is Events<E>")
61    }
62
63    /// Advance every queue one frame (see `Events::update`). Queues are
64    /// independent, so rotation order does not matter.
65    pub fn update_all(&mut self) {
66        for queue in self.queues.values_mut() {
67            queue.update();
68        }
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn get_before_first_write_is_none() {
78        let store = EventStore::new();
79        assert!(store.get::<u32>().is_none());
80    }
81
82    #[test]
83    fn queue_persists_across_accesses() {
84        let mut store = EventStore::new();
85        store.get_mut_or_create::<u32>().send(7);
86        assert_eq!(store.get::<u32>().unwrap().len(), 1);
87        // A second mutable access returns the same queue, not a fresh one.
88        assert_eq!(store.get_mut_or_create::<u32>().len(), 1);
89    }
90
91    #[test]
92    fn update_all_rotates_every_queue() {
93        let mut store = EventStore::new();
94        store.get_mut_or_create::<u32>().send(1);
95        store.get_mut_or_create::<&str>().send("a");
96        // Two-frame retention: one rotation keeps the events readable, the
97        // second retires them from every queue.
98        store.update_all();
99        assert_eq!(store.get::<u32>().unwrap().len(), 1);
100        assert_eq!(store.get::<&str>().unwrap().len(), 1);
101        store.update_all();
102        assert!(store.get::<u32>().unwrap().is_empty());
103        assert!(store.get::<&str>().unwrap().is_empty());
104    }
105}