1use std::any::{Any, TypeId};
32use std::collections::HashMap;
33use std::sync::atomic::{AtomicUsize, Ordering};
34use std::sync::{LazyLock, Mutex, OnceLock};
35
36use crate::entity::entity::Entity;
37use crate::entity::entity_store::register_property_with_entity;
38use crate::entity::events::PartialPropertyChangeEventBox;
39use crate::entity::index::{FullIndex, IndexCountResult, IndexSetResult, ValueCountIndex};
40use crate::entity::multi_property::multi_property_id_for_property_type_id;
41use crate::entity::property::{IndexableProperty, Property};
42use crate::entity::property_list::PropertyList;
43use crate::entity::property_value_store::PropertyValueStore;
44use crate::entity::property_value_store_core::PropertyValueStoreCore;
45use crate::entity::value_change_counter::StratifiedValueChangeCounter;
46use crate::entity::{EntityId, PropertyIndexType};
47use crate::Context;
48
49static NEXT_PROPERTY_ID: LazyLock<Mutex<HashMap<usize, usize>>> =
57 LazyLock::new(|| Mutex::new(HashMap::default()));
58
59#[derive(Default)]
64pub(super) struct PropertyMetadata<E: Entity> {
65 pub dependents: Vec<usize>,
69 #[allow(clippy::type_complexity)]
74 pub value_store_constructor: Option<fn() -> Box<dyn PropertyValueStore<E>>>,
75}
76
77#[allow(clippy::type_complexity)]
81static PROPERTY_METADATA_BUILDER: LazyLock<
82 Mutex<HashMap<(usize, usize), Box<dyn Any + Send + Sync>>>,
83> = LazyLock::new(|| Mutex::new(HashMap::default()));
84
85static PROPERTY_METADATA: OnceLock<HashMap<(usize, usize), Box<dyn Any + Send + Sync>>> =
90 OnceLock::new();
91
92fn property_metadata() -> &'static HashMap<(usize, usize), Box<dyn Any + Send + Sync>> {
94 PROPERTY_METADATA.get_or_init(|| {
95 let mut builder = PROPERTY_METADATA_BUILDER.lock().unwrap();
96 std::mem::take(&mut *builder)
97 })
98}
99
100#[must_use]
106pub(super) fn get_property_dependents_static<E: Entity>(property_index: usize) -> &'static [usize] {
107 let map = property_metadata();
108 let property_metadata = map
109 .get(&(E::id(), property_index))
110 .unwrap_or_else(|| panic!("No registered property found with index = {property_index:?}. You must use the `define_property!` macro to create a registered property."));
111 let property_metadata: &PropertyMetadata<E> = property_metadata.downcast_ref().unwrap_or_else(
112 || panic!(
113 "Property type at index {:?} does not match registered property type. You must use the `define_property!` macro to create a registered property.",
114 property_index
115 )
116 );
117
118 property_metadata.dependents.as_slice()
119}
120
121pub fn add_to_property_registry<E: Entity, P: Property<E>>() {
125 let property_index = P::id();
127
128 register_property_with_entity(
130 <E as Entity>::type_id(),
131 <P as Property<E>>::type_id(),
132 P::is_required(),
133 );
134
135 let mut property_metadata = PROPERTY_METADATA_BUILDER.lock().unwrap();
136 if PROPERTY_METADATA.get().is_some() {
137 panic!(
138 "`add_to_property_registry()` called after property metadata was frozen; registration must occur during startup/ctors."
139 );
140 }
141
142 {
144 let metadata = property_metadata
145 .entry((E::id(), property_index))
146 .or_insert_with(|| Box::new(PropertyMetadata::<E>::default()));
147 let metadata: &mut PropertyMetadata<E> = metadata.downcast_mut().unwrap();
148 metadata
149 .value_store_constructor
150 .get_or_insert(PropertyValueStoreCore::<E, P>::new_boxed);
151 }
152
153 for dependency in P::non_derived_dependencies() {
155 let dependency_meta = property_metadata
157 .entry((E::id(), dependency))
158 .or_insert_with(|| Box::new(PropertyMetadata::<E>::default()));
159 let dependency_meta: &mut PropertyMetadata<E> = dependency_meta.downcast_mut().unwrap();
160 dependency_meta.dependents.push(property_index);
161 }
162}
163
164pub fn get_registered_property_count<E: Entity>() -> usize {
166 let map = NEXT_PROPERTY_ID.lock().unwrap();
167 *map.get(&E::id()).unwrap_or(&0)
168}
169
170pub fn initialize_property_id<E: Entity>(property_id: &AtomicUsize) -> usize {
183 let mut guard = NEXT_PROPERTY_ID.lock().unwrap();
185 let candidate = guard.entry(E::id()).or_insert_with(|| 0);
186
187 match property_id.compare_exchange(usize::MAX, *candidate, Ordering::AcqRel, Ordering::Acquire)
194 {
195 Ok(_) => {
196 *candidate += 1;
198 *candidate - 1
199 }
200 Err(existing) => {
201 existing
204 }
205 }
206}
207
208pub struct PropertyStore<E: Entity> {
210 items: Vec<Box<dyn PropertyValueStore<E>>>,
212}
213
214impl<E: Entity> Default for PropertyStore<E> {
215 fn default() -> Self {
216 PropertyStore::new()
217 }
218}
219
220impl<E: Entity> PropertyStore<E> {
221 #[must_use]
223 pub fn new() -> Self {
224 let num_items = get_registered_property_count::<E>();
225 let property_metadata = property_metadata();
227
228 let items = (0..num_items)
230 .map(|idx| {
231 let metadata = property_metadata
232 .get(&(E::id(), idx))
233 .unwrap_or_else(|| panic!("No property metadata entry for index {idx}"))
234 .downcast_ref::<PropertyMetadata<E>>()
235 .unwrap_or_else(|| {
236 panic!(
237 "Property metadata entry for index {idx} does not match expected type"
238 )
239 });
240 let constructor = metadata
241 .value_store_constructor
242 .unwrap_or_else(|| panic!("No PropertyValueStore constructor for index {idx}"));
243 constructor()
244 })
245 .collect();
246
247 Self { items }
248 }
249
250 #[must_use]
252 pub fn get<P: Property<E>>(&self) -> &PropertyValueStoreCore<E, P> {
253 let index = P::id();
254 let property_value_store =
255 self.items
256 .get(index)
257 .unwrap_or_else(||
258 panic!(
259 "No registered property found with index = {:?} while trying to get property {}. You must use the `define_property!` macro to create a registered property.",
260 index,
261 P::name()
262 )
263 );
264 let property_value_store: &PropertyValueStoreCore<E, P> = property_value_store
265 .as_any()
266 .downcast_ref::<PropertyValueStoreCore<E, P>>()
267 .unwrap_or_else(||
268 {
269 panic!(
270 "Property type at index {:?} does not match registered property type. Found type_id {:?} while getting type_id {:?}. You must use the `define_property!` macro to create a registered property.",
271 index,
272 (**property_value_store).type_id(),
273 TypeId::of::<PropertyValueStoreCore<E, P>>()
274 )
275 }
276 );
277 property_value_store
278 }
279
280 #[must_use]
282 pub fn get_mut<P: Property<E>>(&mut self) -> &mut PropertyValueStoreCore<E, P> {
283 let index = P::id();
284 let property_value_store =
285 self.items
286 .get_mut(index)
287 .unwrap_or_else(||
288 panic!(
289 "No registered property found with index = {:?} while trying to get property {}. You must use the `define_property!` macro to create a registered property.",
290 index,
291 P::name()
292 )
293 );
294 let type_id = (**property_value_store).type_id(); let property_value_store: &mut PropertyValueStoreCore<E, P> = property_value_store
296 .as_any_mut()
297 .downcast_mut::<PropertyValueStoreCore<E, P>>()
298 .unwrap_or_else(||
299 {
300 panic!(
301 "Property type at index {:?} does not match registered property type. Found type_id {:?} while getting type_id {:?}. You must use the `define_property!` macro to create a registered property.",
302 index,
303 type_id,
304 TypeId::of::<PropertyValueStoreCore<E, P>>()
305 )
306 }
307 );
308 property_value_store
309 }
310
311 #[must_use]
314 pub(crate) fn create_partial_property_change(
315 &self,
316 property_index: usize,
317 entity_id: EntityId<E>,
318 context: &Context,
319 ) -> PartialPropertyChangeEventBox {
320 let property_value_store = self.items
321 .get(property_index)
322 .unwrap_or_else(|| panic!("No registered property found with index = {property_index:?}. You must use the `define_property!` macro to create a registered property."));
323
324 property_value_store.create_partial_property_change(entity_id, context)
325 }
326
327 #[must_use]
329 pub(crate) fn should_create_partial_property_change(
330 &self,
331 property_index: usize,
332 context: &Context,
333 ) -> bool {
334 let property_value_store = self.items
335 .get(property_index)
336 .unwrap_or_else(|| panic!("No registered property found with index = {property_index:?}. You must use the `define_property!` macro to create a registered property."));
337
338 property_value_store.should_create_partial_change(context)
339 }
340
341 #[cfg(test)]
344 #[must_use]
345 pub fn is_property_indexed<P: Property<E>>(&self) -> bool {
346 self.items
347 .get(P::id())
348 .unwrap_or_else(|| panic!("No registered property {} found with id = {:?}. You must use the `define_property!` macro to create a registered property.", P::name(), P::id()))
349 .index_type()
350 != PropertyIndexType::Unindexed
351 }
352
353 pub fn set_property_indexed<P: IndexableProperty<E>>(&mut self, index_type: PropertyIndexType) {
358 if index_type != PropertyIndexType::Unindexed {
359 if let Some((representative_id, representative_name)) =
360 multi_property_id_for_property_type_id(E::id(), P::type_id())
361 {
362 if representative_id != P::id() {
363 panic!(
364 "Cannot index multi-property {} because it is equivalent to representative multi-property {}. Index the representative multi-property instead.",
365 P::name(),
366 representative_name
367 );
368 }
369 }
370 }
371
372 let property_value_store = self.items
373 .get_mut(P::id())
374 .unwrap_or_else(|| panic!("No registered property {} found with id = {:?}. You must use the `define_property!` macro to create a registered property.", P::name(), P::id()));
375 let property_value_store = property_value_store
376 .as_any_mut()
377 .downcast_mut::<PropertyValueStoreCore<E, P>>()
378 .unwrap_or_else(|| {
379 panic!(
380 "Property type at index {:?} does not match registered property type. You must use the `define_property!` macro to create a registered property.",
381 P::id()
382 )
383 });
384 match index_type {
385 PropertyIndexType::Unindexed => {
386 property_value_store.index = None;
387 }
388 PropertyIndexType::FullIndex => {
389 if property_value_store.index_type() != PropertyIndexType::FullIndex {
390 property_value_store.index = Some(Box::new(FullIndex::<E, P>::new()));
391 }
392 }
393 PropertyIndexType::ValueCountIndex => {
394 if property_value_store.index_type() != PropertyIndexType::ValueCountIndex {
395 property_value_store.index = Some(Box::new(ValueCountIndex::<E, P>::new()));
396 }
397 }
398 }
399 }
400
401 #[must_use]
405 pub fn create_value_change_counter<PL, P>(&mut self) -> usize
406 where
407 PL: PropertyList<E> + Eq + std::hash::Hash,
408 P: Property<E> + Eq + std::hash::Hash,
409 {
410 let property_value_store = self.get_mut::<P>();
411 property_value_store.add_value_change_counter(Box::new(StratifiedValueChangeCounter::<
412 E,
413 PL,
414 P,
415 >::new()))
416 }
417
418 pub fn index_unindexed_entities_for_property_id(
421 &mut self,
422 context: &Context,
423 property_id: usize,
424 ) {
425 self.items[property_id].index_unindexed_entities(context)
426 }
427
428 pub fn index_unindexed_entities_for_all_properties(&mut self, context: &Context) {
430 for store in &mut self.items {
431 store.index_unindexed_entities(context);
432 }
433 }
434
435 #[must_use]
436 pub fn get_index_set_for_query_parts(
437 &self,
438 property_id: usize,
439 query_parts: &[&dyn Any],
440 ) -> IndexSetResult<'_, E> {
441 self.items[property_id].get_index_set_for_query_parts(query_parts)
442 }
443
444 #[must_use]
445 pub fn get_index_count_for_query_parts(
446 &self,
447 property_id: usize,
448 query_parts: &[&dyn Any],
449 ) -> IndexCountResult {
450 self.items[property_id].get_index_count_for_query_parts(query_parts)
451 }
452}
453
454#[cfg(test)]
455mod tests {
456 #![allow(dead_code)]
457 use std::any::Any;
458
459 use super::*;
460 use crate::entity::index::{IndexCountResult, IndexSetResult};
461 use crate::prelude::*;
462 use crate::{define_entity, define_property, with, Context};
463
464 define_entity!(Person);
465
466 define_property!(struct Age(u8), Person);
467 define_property!(
468 enum InfectionStatus {
469 Susceptible,
470 Infected,
471 Recovered,
472 },
473 Person,
474 default_const = InfectionStatus::Susceptible
475 );
476 define_property!(struct Vaccinated(bool), Person, default_const = Vaccinated(false));
477
478 #[test]
479 fn property_store_default_matches_new() {
480 let property_store = PropertyStore::<Person>::default();
481 assert_eq!(
482 property_store.items.len(),
483 get_registered_property_count::<Person>()
484 );
485 }
486
487 #[test]
488 fn test_get_property_store() {
489 let mut property_store = PropertyStore::new();
490
491 {
492 let ages: &mut PropertyValueStoreCore<_, Age> = property_store.get_mut();
493 ages.set(EntityId::<Person>::new(0), Age(12));
494 ages.set(EntityId::<Person>::new(1), Age(33));
495 ages.set(EntityId::<Person>::new(2), Age(44));
496
497 let infection_statuses: &mut PropertyValueStoreCore<_, InfectionStatus> =
498 property_store.get_mut();
499 infection_statuses.set(EntityId::<Person>::new(0), InfectionStatus::Susceptible);
500 infection_statuses.set(EntityId::<Person>::new(1), InfectionStatus::Susceptible);
501 infection_statuses.set(EntityId::<Person>::new(2), InfectionStatus::Infected);
502
503 let vaccine_status: &mut PropertyValueStoreCore<_, Vaccinated> =
504 property_store.get_mut();
505 vaccine_status.set(EntityId::<Person>::new(0), Vaccinated(true));
506 vaccine_status.set(EntityId::<Person>::new(1), Vaccinated(false));
507 vaccine_status.set(EntityId::<Person>::new(2), Vaccinated(true));
508 }
509
510 {
512 let ages: &PropertyValueStoreCore<_, Age> = property_store.get();
513 assert_eq!(ages.get(EntityId::<Person>::new(0)), Age(12));
514 assert_eq!(ages.get(EntityId::<Person>::new(1)), Age(33));
515 assert_eq!(ages.get(EntityId::<Person>::new(2)), Age(44));
516
517 let infection_statuses: &PropertyValueStoreCore<_, InfectionStatus> =
518 property_store.get();
519 assert_eq!(
520 infection_statuses.get(EntityId::<Person>::new(0)),
521 InfectionStatus::Susceptible
522 );
523 assert_eq!(
524 infection_statuses.get(EntityId::<Person>::new(1)),
525 InfectionStatus::Susceptible
526 );
527 assert_eq!(
528 infection_statuses.get(EntityId::<Person>::new(2)),
529 InfectionStatus::Infected
530 );
531
532 let vaccine_status: &PropertyValueStoreCore<_, Vaccinated> = property_store.get();
533 assert_eq!(
534 vaccine_status.get(EntityId::<Person>::new(0)),
535 Vaccinated(true)
536 );
537 assert_eq!(
538 vaccine_status.get(EntityId::<Person>::new(1)),
539 Vaccinated(false)
540 );
541 assert_eq!(
542 vaccine_status.get(EntityId::<Person>::new(2)),
543 Vaccinated(true)
544 );
545 }
546 }
547
548 #[test]
549 fn test_index_query_results_for_property_store() {
550 let mut context = Context::new();
551 context.index_property::<Person, Age>();
552
553 let existing_value = Age(12);
554 let missing_value = Age(99);
555 let existing_query_parts = [&existing_value as &dyn Any];
556 let missing_query_parts = [&missing_value as &dyn Any];
557
558 let _ = context.add_entity(with!(Person, existing_value)).unwrap();
559 let _ = context.add_entity(with!(Person, existing_value)).unwrap();
560
561 let property_store = context.entity_store.get_property_store::<Person>();
562
563 assert_eq!(
565 property_store.get_index_count_for_query_parts(Age::id(), &missing_query_parts,),
566 IndexCountResult::Count(0)
567 );
568 assert_eq!(
569 property_store.get_index_count_for_query_parts(Age::id(), &existing_query_parts,),
570 IndexCountResult::Count(2)
571 );
572
573 assert!(matches!(
575 property_store.get_index_set_for_query_parts(Age::id(), &missing_query_parts,),
576 IndexSetResult::Empty
577 ));
578 assert!(matches!(
579 property_store.get_index_set_for_query_parts(
580 Age::id(),
581 &existing_query_parts,
582 ),
583 IndexSetResult::Set(set) if set.len() == 2
584 ));
585 }
586
587 #[test]
588 fn test_index_query_results_for_property_store_value_count_index() {
589 let mut context = Context::new();
590 context.index_property_counts::<Person, Age>();
591
592 let existing_value = Age(12);
593 let missing_value = Age(99);
594 let existing_query_parts = [&existing_value as &dyn Any];
595 let missing_query_parts = [&missing_value as &dyn Any];
596
597 let _ = context.add_entity(with!(Person, existing_value)).unwrap();
598 let _ = context.add_entity(with!(Person, existing_value)).unwrap();
599
600 let property_store = context.entity_store.get_property_store::<Person>();
601
602 assert_eq!(
604 property_store.get_index_count_for_query_parts(Age::id(), &missing_query_parts,),
605 IndexCountResult::Count(0)
606 );
607 assert_eq!(
608 property_store.get_index_count_for_query_parts(Age::id(), &existing_query_parts,),
609 IndexCountResult::Count(2)
610 );
611
612 assert!(matches!(
614 property_store.get_index_set_for_query_parts(Age::id(), &missing_query_parts,),
615 IndexSetResult::Unsupported
616 ));
617 assert!(matches!(
618 property_store.get_index_set_for_query_parts(Age::id(), &existing_query_parts,),
619 IndexSetResult::Unsupported
620 ));
621 }
622
623 #[test]
624 fn test_index_query_results_for_property_store_unindexed() {
625 let mut context = Context::new();
626 let existing_value = Age(12);
627 let missing_value = Age(99);
628 let existing_query_parts = [&existing_value as &dyn Any];
629 let missing_query_parts = [&missing_value as &dyn Any];
630
631 let _ = context.add_entity(with!(Person, existing_value)).unwrap();
632 let _ = context.add_entity(with!(Person, existing_value)).unwrap();
633
634 let property_store = context.entity_store.get_property_store::<Person>();
635
636 assert_eq!(
638 property_store.get_index_count_for_query_parts(Age::id(), &missing_query_parts,),
639 IndexCountResult::Unsupported
640 );
641 assert_eq!(
642 property_store.get_index_count_for_query_parts(Age::id(), &existing_query_parts,),
643 IndexCountResult::Unsupported
644 );
645
646 assert!(matches!(
648 property_store.get_index_set_for_query_parts(Age::id(), &missing_query_parts,),
649 IndexSetResult::Unsupported
650 ));
651 assert!(matches!(
652 property_store.get_index_set_for_query_parts(Age::id(), &existing_query_parts,),
653 IndexSetResult::Unsupported
654 ));
655 }
656}