1use alloc::collections::BTreeMap;
7use alloc::string::{String, ToString};
8use alloc::vec::Vec;
9use core::any::{type_name, TypeId};
10
11use crate::operation::StageOperation;
12use crate::world::WorldOwner;
13
14#[derive(Clone, Debug, Eq, PartialEq, Hash)]
16pub struct EventId {
17 owner: WorldOwner,
18 index: u32,
19}
20
21#[derive(Copy, Clone, Debug, Eq, PartialEq)]
23pub enum EventRetention {
24 Frame(StageOperation),
26 Manual,
28 Bounded(usize),
30}
31
32#[derive(Copy, Clone, Debug, Eq, PartialEq)]
34pub struct EventOptions {
35 retention: EventRetention,
36 external_source: bool,
37}
38
39impl EventOptions {
40 pub fn frame(operation: StageOperation) -> Self {
42 Self {
43 retention: EventRetention::Frame(operation),
44 external_source: false,
45 }
46 }
47
48 pub fn manual() -> Self {
50 Self {
51 retention: EventRetention::Manual,
52 external_source: false,
53 }
54 }
55
56 pub fn bounded(capacity: usize) -> Result<Self, EventRegistrationError> {
58 if capacity == 0 {
59 return Err(EventRegistrationError::InvalidCapacity);
60 }
61 Ok(Self {
62 retention: EventRetention::Bounded(capacity),
63 external_source: false,
64 })
65 }
66
67 pub fn external_source(mut self) -> Self {
69 self.external_source = true;
70 self
71 }
72
73 pub(crate) fn retention(self) -> EventRetention {
74 self.retention
75 }
76
77 #[allow(dead_code)]
78 pub(crate) fn is_external_source(self) -> bool {
79 self.external_source
80 }
81}
82
83#[non_exhaustive]
85#[derive(Clone, Debug, Eq, PartialEq)]
86pub enum EventRegistrationError {
87 TypeConflict {
89 name: String,
91 existing: String,
93 requested: String,
95 },
96 InvalidCapacity,
98}
99
100struct EventEntry {
101 name: String,
102 type_id: TypeId,
103 options: EventOptions,
104 #[allow(dead_code)]
105 lifecycle_component_index: Option<usize>,
106}
107
108pub(crate) struct EventRegistry {
109 entries: Vec<EventEntry>,
110 ordinary_by_type: BTreeMap<TypeId, u32>,
111 ordinary_count: usize,
112}
113
114const LINEAR_TYPE_LOOKUP_PREFIX: usize = 16;
115
116impl EventId {
117 pub(crate) fn new(owner: WorldOwner, index: u32) -> Self {
118 Self { owner, index }
119 }
120
121 pub fn index(&self) -> usize {
123 self.index as usize
124 }
125
126 pub(crate) fn validate_owner(&self, owner: &WorldOwner) -> Result<(), EventRegistrationError> {
127 if self.owner.same(owner) {
128 Ok(())
129 } else {
130 Err(EventRegistrationError::TypeConflict {
131 name: String::from("<event>"),
132 existing: String::from("different world"),
133 requested: String::from("different world"),
134 })
135 }
136 }
137}
138
139impl EventRegistry {
140 pub fn new() -> Self {
141 Self {
142 entries: Vec::new(),
143 ordinary_by_type: BTreeMap::new(),
144 ordinary_count: 0,
145 }
146 }
147
148 pub fn len(&self) -> usize {
149 self.entries.len()
150 }
151
152 pub fn register<E: Clone + 'static>(
153 &mut self,
154 owner: &WorldOwner,
155 options: EventOptions,
156 ) -> Result<EventId, EventRegistrationError> {
157 let name = type_name::<E>().to_string();
158 let type_id = TypeId::of::<E>();
159 if let Some(index) = self.ordinary_index_of_type_id(type_id) {
160 let index = index as usize;
161 let entry = &self.entries[index];
162 if entry.options == options {
163 return Ok(EventId::new(owner.clone(), index as u32));
164 }
165 return Err(EventRegistrationError::TypeConflict {
166 name: name.clone(),
167 existing: entry.name.clone(),
168 requested: name,
169 });
170 }
171 if let Some((_index, entry)) = self
172 .entries
173 .iter()
174 .enumerate()
175 .find(|(_, entry)| entry.name == name)
176 {
177 return Err(EventRegistrationError::TypeConflict {
178 name: name.clone(),
179 existing: entry.name.clone(),
180 requested: name,
181 });
182 }
183 let index = self.entries.len() as u32;
184 self.entries.push(EventEntry {
185 name,
186 type_id,
187 options,
188 lifecycle_component_index: None,
189 });
190 if self.ordinary_count >= LINEAR_TYPE_LOOKUP_PREFIX {
191 self.ordinary_by_type.insert(type_id, index);
192 }
193 self.ordinary_count += 1;
194 Ok(EventId::new(owner.clone(), index))
195 }
196
197 pub(crate) fn register_lifecycle<E: Clone + 'static>(
198 &mut self,
199 owner: &WorldOwner,
200 component_index: usize,
201 kind: crate::event::component::LifecycleKind,
202 options: EventOptions,
203 ) -> Result<EventId, EventRegistrationError> {
204 let name = alloc::format!("__lifecycle_{kind:?}_{component_index}");
205 let type_id = TypeId::of::<E>();
206 let index = self.entries.len() as u32;
207 self.entries.push(EventEntry {
208 name,
209 type_id,
210 options,
211 lifecycle_component_index: Some(component_index),
212 });
213 Ok(EventId::new(owner.clone(), index))
214 }
215
216 pub fn options(&self, id: &EventId) -> Option<EventOptions> {
217 self.entries.get(id.index()).map(|entry| entry.options)
218 }
219
220 #[allow(dead_code)]
221 pub fn type_id(&self, id: &EventId) -> Option<TypeId> {
222 self.entries.get(id.index()).map(|entry| entry.type_id)
223 }
224
225 pub fn id_of<E: Clone + 'static>(&self, owner: &WorldOwner) -> Option<EventId> {
226 self.id_of_type_id(owner, TypeId::of::<E>())
227 }
228
229 pub(crate) fn id_of_type_id(&self, owner: &WorldOwner, type_id: TypeId) -> Option<EventId> {
230 self.ordinary_index_of_type_id(type_id)
231 .map(|index| EventId::new(owner.clone(), index))
232 }
233
234 fn ordinary_index_of_type_id(&self, type_id: TypeId) -> Option<u32> {
235 let mut ordinary_entries = 0;
236 for (index, entry) in self.entries.iter().enumerate() {
237 if entry.lifecycle_component_index.is_some() {
238 continue;
239 }
240 ordinary_entries += 1;
241 if entry.type_id == type_id {
242 return Some(index as u32);
243 }
244 if ordinary_entries == LINEAR_TYPE_LOOKUP_PREFIX {
245 break;
246 }
247 }
248 self.ordinary_by_type.get(&type_id).copied()
249 }
250}
251
252impl Default for EventRegistry {
253 fn default() -> Self {
254 Self::new()
255 }
256}
257
258#[cfg(feature = "std")]
259impl core::fmt::Display for EventRegistrationError {
260 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
261 match self {
262 Self::TypeConflict {
263 name,
264 existing,
265 requested,
266 } => write!(
267 f,
268 "event registration conflict for {name}: existing={existing}, requested={requested}"
269 ),
270 Self::InvalidCapacity => f.write_str("event retention capacity must be nonzero"),
271 }
272 }
273}
274
275#[cfg(feature = "std")]
276impl std::error::Error for EventRegistrationError {}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281
282 #[derive(Clone, Copy)]
283 struct Damage(#[allow(dead_code)] u32);
284
285 #[derive(Clone, Copy)]
286 struct Heal(#[allow(dead_code)] u32);
287
288 #[derive(Clone, Copy)]
289 struct Indexed<const N: usize>;
290
291 #[test]
292 fn external_source_flag_round_trip() {
293 let options = EventOptions::manual().external_source();
294 assert!(options.is_external_source());
295 assert_eq!(options.retention(), EventRetention::Manual);
296 }
297
298 #[test]
299 fn default_registry_is_empty() {
300 let registry = EventRegistry::default();
301 assert_eq!(registry.len(), 0);
302 }
303
304 #[test]
305 fn duplicate_registration_is_idempotent() {
306 let owner = WorldOwner::new();
307 let mut registry = EventRegistry::new();
308 let options = EventOptions::manual();
309 let first = registry.register::<Damage>(&owner, options).expect("first");
310 let second = registry
311 .register::<Damage>(&owner, options)
312 .expect("repeat");
313 assert_eq!(first, second);
314 }
315
316 #[test]
317 fn same_name_different_type_is_rejected() {
318 let owner = WorldOwner::new();
319 let mut registry = EventRegistry::new();
320 let name = type_name::<Damage>().to_string();
321 registry.entries.push(EventEntry {
322 name: name.clone(),
323 type_id: TypeId::of::<Heal>(),
324 options: EventOptions::manual(),
325 lifecycle_component_index: None,
326 });
327 let err = registry
328 .register::<Damage>(&owner, EventOptions::manual())
329 .expect_err("conflict");
330 assert!(matches!(err, EventRegistrationError::TypeConflict { .. }));
331 }
332
333 #[test]
334 fn event_id_validate_owner_rejects_foreign_world() {
335 let owner_a = WorldOwner::new();
336 let owner_b = WorldOwner::new();
337 let id = EventId::new(owner_a, 0);
338 assert!(registry_owner_check(&id, &owner_b).is_err());
339 }
340
341 #[test]
342 fn type_id_accessor_returns_registered_type() {
343 let owner = WorldOwner::new();
344 let mut registry = EventRegistry::new();
345 let id = registry
346 .register::<Damage>(&owner, EventOptions::manual())
347 .expect("register");
348 assert_eq!(registry.type_id(&id), Some(TypeId::of::<Damage>()));
349 }
350
351 #[test]
352 fn ordinary_type_index_ignores_lifecycle_entries_of_the_same_payload_type() {
353 use crate::event::component::LifecycleKind;
354
355 let owner = WorldOwner::new();
356 let mut registry = EventRegistry::new();
357 let lifecycle = registry
358 .register_lifecycle::<Damage>(
359 &owner,
360 0,
361 LifecycleKind::Added,
362 EventOptions::frame(StageOperation::Update),
363 )
364 .expect("lifecycle");
365 assert!(registry.id_of::<Damage>(&owner).is_none());
366
367 let ordinary = registry
368 .register::<Damage>(&owner, EventOptions::manual())
369 .expect("ordinary");
370 assert_ne!(ordinary, lifecycle);
371 assert_eq!(registry.id_of::<Damage>(&owner), Some(ordinary));
372 }
373
374 #[test]
375 fn adaptive_type_lookup_resolves_prefix_and_tree_entries() {
376 let owner = WorldOwner::new();
377 let mut registry = EventRegistry::new();
378 macro_rules! register {
379 ($index:literal) => {
380 registry
381 .register::<Indexed<$index>>(&owner, EventOptions::manual())
382 .expect("register");
383 };
384 }
385 register!(0);
386 register!(1);
387 register!(2);
388 register!(3);
389 register!(4);
390 register!(5);
391 register!(6);
392 register!(7);
393 register!(8);
394 register!(9);
395 register!(10);
396 register!(11);
397 register!(12);
398 register!(13);
399 register!(14);
400 register!(15);
401 register!(16);
402
403 assert_eq!(registry.ordinary_count, 17);
404 assert_eq!(registry.ordinary_by_type.len(), 1);
405
406 assert_eq!(
407 registry.id_of::<Indexed<0>>(&owner).map(|id| id.index()),
408 Some(0)
409 );
410 assert_eq!(
411 registry.id_of::<Indexed<16>>(&owner).map(|id| id.index()),
412 Some(16)
413 );
414 assert_eq!(
415 registry
416 .register::<Indexed<16>>(&owner, EventOptions::manual())
417 .expect("duplicate fallback entry")
418 .index(),
419 16
420 );
421 }
422
423 fn registry_owner_check(
424 id: &EventId,
425 owner: &WorldOwner,
426 ) -> Result<(), EventRegistrationError> {
427 id.validate_owner(owner)
428 }
429}