1use std::any::{Any, TypeId};
32use std::collections::HashMap;
33use std::sync::atomic::{AtomicUsize, Ordering};
34use std::sync::{LazyLock, Mutex, OnceLock};
35
36use crate::data_structures::bit_set::BitSet;
37use crate::entity::entity::Entity;
38use crate::entity::entity_store::register_property_with_entity;
39use crate::entity::events::PartialPropertyChangeEventBox;
40use crate::entity::index::{IndexCountResult, IndexSetResult, PropertyIndex};
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;
47use crate::{Context, ContextEntitiesExt};
48
49pub(in crate::entity) type IndexNewEntityFn<E> = fn(&mut Context, EntityId<E>);
50
51fn index_new_entity<E, P>(context: &mut Context, entity_id: EntityId<E>)
52where
53 E: Entity,
54 P: IndexableProperty<E>,
55{
56 let value: P = context.get_property(entity_id);
59
60 let property_value_store = context.get_property_value_store_mut::<E, P>();
61 let index = property_value_store
62 .index
63 .as_mut()
64 .expect("Ixa internal error: index_new_entity dispatch invoked for an unindexed property");
65
66 index.add_entity(&value, entity_id);
67}
68
69static NEXT_PROPERTY_ID: LazyLock<Mutex<HashMap<usize, usize>>> =
77 LazyLock::new(|| Mutex::new(HashMap::default()));
78
79#[cfg(feature = "profiling")]
82static PROPERTY_NAMES: LazyLock<Mutex<HashMap<(usize, TypeId), &'static str>>> =
83 LazyLock::new(|| Mutex::new(HashMap::default()));
84
85#[cfg(feature = "profiling")]
86pub(crate) fn registered_property_name(entity_id: usize, property_type_id: TypeId) -> &'static str {
87 PROPERTY_NAMES
88 .lock()
89 .unwrap()
90 .get(&(entity_id, property_type_id))
91 .unwrap_or_else(|| {
92 panic!(
93 "No registered property name for entity ID {entity_id} and logical property type ID {property_type_id:?}"
94 )
95 })
96}
97
98#[derive(Default)]
103pub(super) struct PropertyMetadata<E: Entity> {
104 pub dependents: Vec<usize>,
108 #[allow(clippy::type_complexity)]
113 pub value_store_constructor: Option<fn() -> Box<dyn PropertyValueStore<E>>>,
114}
115
116#[allow(clippy::type_complexity)]
120static PROPERTY_METADATA_BUILDER: LazyLock<
121 Mutex<HashMap<(usize, usize), Box<dyn Any + Send + Sync>>>,
122> = LazyLock::new(|| Mutex::new(HashMap::default()));
123
124static PROPERTY_METADATA: OnceLock<HashMap<(usize, usize), Box<dyn Any + Send + Sync>>> =
129 OnceLock::new();
130
131fn property_metadata() -> &'static HashMap<(usize, usize), Box<dyn Any + Send + Sync>> {
133 PROPERTY_METADATA.get_or_init(|| {
134 let mut builder = PROPERTY_METADATA_BUILDER.lock().unwrap();
135 std::mem::take(&mut *builder)
136 })
137}
138
139#[must_use]
145pub(super) fn get_property_dependents_static<E: Entity>(property_index: usize) -> &'static [usize] {
146 let map = property_metadata();
147 let property_metadata = map
148 .get(&(E::id(), property_index))
149 .unwrap_or_else(|| panic!("No registered property found with index = {property_index:?}. You must use the `define_property!` macro to create a registered property."));
150 let property_metadata: &PropertyMetadata<E> = property_metadata.downcast_ref().unwrap_or_else(
151 || panic!(
152 "Property type at index {:?} does not match registered property type. You must use the `define_property!` macro to create a registered property.",
153 property_index
154 )
155 );
156
157 property_metadata.dependents.as_slice()
158}
159
160pub fn add_to_property_registry<E: Entity, P: Property<E>>() {
164 let property_index = P::id();
166
167 #[cfg(feature = "profiling")]
168 {
169 let key = (E::id(), P::type_id());
170 let mut names = PROPERTY_NAMES.lock().unwrap();
171 if let Some(existing) = names.insert(key, P::name()) {
172 assert_eq!(
173 existing,
174 P::name(),
175 "conflicting names for one logical property identity"
176 );
177 }
178 }
179
180 register_property_with_entity(
182 <E as Entity>::type_id(),
183 <P as Property<E>>::type_id(),
184 P::is_required(),
185 );
186
187 let mut property_metadata = PROPERTY_METADATA_BUILDER.lock().unwrap();
188 if PROPERTY_METADATA.get().is_some() {
189 panic!(
190 "`add_to_property_registry()` called after property metadata was frozen; registration must occur during startup/ctors."
191 );
192 }
193
194 {
196 let metadata = property_metadata
197 .entry((E::id(), property_index))
198 .or_insert_with(|| Box::new(PropertyMetadata::<E>::default()));
199 let metadata: &mut PropertyMetadata<E> = metadata
200 .downcast_mut()
201 .expect("Ixa internal error: property metadata has the wrong entity type");
202 metadata
203 .value_store_constructor
204 .get_or_insert(PropertyValueStoreCore::<E, P>::new_boxed);
205 }
206
207 for dependency in P::non_derived_dependencies() {
209 let dependency_meta = property_metadata
211 .entry((E::id(), dependency))
212 .or_insert_with(|| Box::new(PropertyMetadata::<E>::default()));
213 let dependency_meta: &mut PropertyMetadata<E> = dependency_meta
214 .downcast_mut()
215 .expect("Ixa internal error: dependency metadata has the wrong entity type");
216 dependency_meta.dependents.push(property_index);
217 }
218}
219
220pub fn get_registered_property_count<E: Entity>() -> usize {
222 let map = NEXT_PROPERTY_ID.lock().unwrap();
223 *map.get(&E::id()).unwrap_or(&0)
224}
225
226pub fn initialize_property_id<E: Entity>(property_id: &AtomicUsize) -> usize {
239 let mut guard = NEXT_PROPERTY_ID.lock().unwrap();
241 let candidate = guard.entry(E::id()).or_insert_with(|| 0);
242
243 let existing = property_id.load(Ordering::Acquire);
245 if existing != usize::MAX {
246 return existing;
247 }
248
249 match property_id.compare_exchange(usize::MAX, *candidate, Ordering::AcqRel, Ordering::Acquire)
256 {
257 Ok(_) => {
258 *candidate += 1;
260 *candidate - 1
261 }
262 Err(existing) => {
263 existing
266 }
267 }
268}
269
270pub struct PropertyStore<E: Entity> {
272 items: Vec<Box<dyn PropertyValueStore<E>>>,
274
275 pub(in crate::entity) property_initialized_event_subscriptions: BitSet,
281
282 pub(in crate::entity) index_new_entity_fns: Vec<(usize, IndexNewEntityFn<E>)>,
285}
286
287impl<E: Entity> Default for PropertyStore<E> {
288 fn default() -> Self {
289 PropertyStore::new()
290 }
291}
292
293impl<E: Entity> PropertyStore<E> {
294 #[must_use]
296 pub fn new() -> Self {
297 let num_items = get_registered_property_count::<E>();
298 let property_metadata = property_metadata();
300
301 let items = (0..num_items)
303 .map(|idx| {
304 let metadata = property_metadata
305 .get(&(E::id(), idx))
306 .unwrap_or_else(|| {
307 panic!("Ixa internal error: no property metadata entry for index {idx}")
308 })
309 .downcast_ref::<PropertyMetadata<E>>()
310 .unwrap_or_else(|| {
311 panic!(
312 "Ixa internal error: property metadata entry for index {idx} does not \
313 match the expected type"
314 )
315 });
316 let constructor = metadata.value_store_constructor.unwrap_or_else(|| {
317 panic!("Ixa internal error: no PropertyValueStore constructor for index {idx}")
318 });
319 constructor()
320 })
321 .collect();
322
323 Self {
324 items,
325 property_initialized_event_subscriptions: BitSet::new(num_items),
326 index_new_entity_fns: Vec::new(),
327 }
328 }
329
330 #[must_use]
332 pub fn get<P: Property<E>>(&self) -> &PropertyValueStoreCore<E, P> {
333 let index = P::id();
334 let property_value_store =
335 self.items
336 .get(index)
337 .unwrap_or_else(||
338 panic!(
339 "No registered property found with index = {:?} while trying to get property {}. You must use the `define_property!` macro to create a registered property.",
340 index,
341 P::name()
342 )
343 );
344 let property_value_store: &PropertyValueStoreCore<E, P> = property_value_store
345 .as_any()
346 .downcast_ref::<PropertyValueStoreCore<E, P>>()
347 .unwrap_or_else(||
348 {
349 panic!(
350 "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.",
351 index,
352 (**property_value_store).type_id(),
353 TypeId::of::<PropertyValueStoreCore<E, P>>()
354 )
355 }
356 );
357 property_value_store
358 }
359
360 #[must_use]
362 pub fn get_mut<P: Property<E>>(&mut self) -> &mut PropertyValueStoreCore<E, P> {
363 let index = P::id();
364 let property_value_store =
365 self.items
366 .get_mut(index)
367 .unwrap_or_else(||
368 panic!(
369 "No registered property found with index = {:?} while trying to get property {}. You must use the `define_property!` macro to create a registered property.",
370 index,
371 P::name()
372 )
373 );
374 let type_id = (**property_value_store).type_id(); let property_value_store: &mut PropertyValueStoreCore<E, P> = property_value_store
376 .as_any_mut()
377 .downcast_mut::<PropertyValueStoreCore<E, P>>()
378 .unwrap_or_else(||
379 {
380 panic!(
381 "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.",
382 index,
383 type_id,
384 TypeId::of::<PropertyValueStoreCore<E, P>>()
385 )
386 }
387 );
388 property_value_store
389 }
390
391 #[must_use]
394 pub(crate) fn create_partial_property_change(
395 &self,
396 property_index: usize,
397 entity_id: EntityId<E>,
398 context: &Context,
399 ) -> PartialPropertyChangeEventBox {
400 let property_value_store = self.items.get(property_index).unwrap_or_else(|| {
401 panic!(
402 "Ixa internal error: dependent property index {property_index:?} is not registered"
403 )
404 });
405
406 property_value_store.create_partial_property_change(entity_id, context)
407 }
408
409 #[must_use]
411 pub(crate) fn should_create_partial_property_change(
412 &self,
413 property_index: usize,
414 context: &Context,
415 ) -> bool {
416 let property_value_store = self.items.get(property_index).unwrap_or_else(|| {
417 panic!(
418 "Ixa internal error: dependent property index {property_index:?} is not registered"
419 )
420 });
421
422 property_value_store.should_create_partial_change(context)
423 }
424
425 #[cfg(test)]
428 #[must_use]
429 pub fn is_property_indexed<P: Property<E>>(&self) -> bool {
430 self.get::<P>().index.is_some()
431 }
432
433 pub(in crate::entity) fn install_property_index<P>(
434 &mut self,
435 new_index: Option<Box<dyn PropertyIndex<E, P>>>,
436 ) where
437 P: IndexableProperty<E>,
438 {
439 let property_id = P::id();
440 let was_indexed = self.get::<P>().index.is_some();
441 let will_be_indexed = new_index.is_some();
442
443 let dispatcher_position = self
446 .index_new_entity_fns
447 .iter()
448 .position(|(id, _)| *id == property_id);
449
450 if was_indexed {
451 assert!(
452 dispatcher_position.is_some(),
453 "Ixa internal error: indexed property is missing its index_new_entity dispatcher",
454 );
455 } else {
456 debug_assert!(
457 dispatcher_position.is_none(),
458 "Ixa internal error: unindexed property unexpectedly has an index_new_entity dispatcher",
459 );
460 }
461
462 if !was_indexed && will_be_indexed {
465 self.index_new_entity_fns.reserve(1);
466 }
467
468 self.get_mut::<P>().index = new_index;
471
472 match (was_indexed, will_be_indexed) {
473 (false, true) => {
474 self.index_new_entity_fns
475 .push((property_id, index_new_entity::<E, P> as IndexNewEntityFn<E>));
476 }
477 (true, false) => {
478 self.index_new_entity_fns
480 .swap_remove(dispatcher_position.unwrap());
481 }
482 _ => {}
483 }
484 }
485
486 #[must_use]
490 pub fn create_value_change_counter<PL, P>(&mut self) -> usize
491 where
492 PL: PropertyList<E> + Eq + std::hash::Hash,
493 P: Property<E> + Eq + std::hash::Hash,
494 {
495 let property_value_store = self.get_mut::<P>();
496 property_value_store.add_value_change_counter(Box::new(StratifiedValueChangeCounter::<
497 E,
498 PL,
499 P,
500 >::new()))
501 }
502
503 #[must_use]
504 pub fn get_index_set_for_query_parts(
505 &self,
506 property_id: usize,
507 query_parts: &[&dyn Any],
508 ) -> IndexSetResult<'_, E> {
509 self.items[property_id].get_index_set_for_query_parts(query_parts)
510 }
511
512 #[must_use]
513 pub fn get_index_count_for_query_parts(
514 &self,
515 property_id: usize,
516 query_parts: &[&dyn Any],
517 ) -> IndexCountResult {
518 self.items[property_id].get_index_count_for_query_parts(query_parts)
519 }
520}
521
522#[cfg(test)]
523mod tests {
524 #![allow(dead_code)]
525 use std::any::Any;
526 use std::panic::{catch_unwind, AssertUnwindSafe};
527
528 use super::*;
529 use crate::entity::index::{FullIndex, IndexCountResult, IndexSetResult, ValueCountIndex};
530 use crate::entity::PropertyIndexType;
531 use crate::prelude::*;
532 use crate::{define_derived_property, define_entity, define_property, with, Context};
533
534 define_entity!(Person);
535
536 define_property!(struct Age(u8), Person);
537 define_property!(
538 enum InfectionStatus {
539 Susceptible,
540 Infected,
541 Recovered,
542 },
543 Person,
544 default_const = InfectionStatus::Susceptible
545 );
546 define_property!(struct Vaccinated(bool), Person, default_const = Vaccinated(false));
547 define_property!(struct PanicDependency(u8), Person, default_const = PanicDependency(0));
548
549 define_derived_property!(
550 struct PanickingDerived(u8),
551 Person,
552 [PanicDependency],
553 [],
554 |dependency| {
555 let dependency: PanicDependency = dependency;
556 assert_ne!(dependency, PanicDependency(255), "sentinel property value");
557 PanickingDerived(dependency.0)
558 }
559 );
560
561 #[test]
562 fn property_store_default_matches_new() {
563 let property_store = PropertyStore::<Person>::default();
564 assert_eq!(
565 property_store.items.len(),
566 get_registered_property_count::<Person>()
567 );
568 }
569
570 #[test]
571 fn install_property_index_maintains_active_dispatchers() {
572 let mut context = Context::new();
573
574 {
575 let property_store = context.entity_store.get_property_store_mut::<Person>();
576 assert_eq!(property_store.index_new_entity_fns.len(), 0);
577
578 property_store.install_property_index::<Age>(Some(Box::new(ValueCountIndex::new())));
579 assert_eq!(property_store.index_new_entity_fns.len(), 1);
580
581 property_store.install_property_index::<Age>(Some(Box::new(ValueCountIndex::new())));
582 assert_eq!(property_store.index_new_entity_fns.len(), 1);
583
584 property_store.install_property_index::<Age>(Some(Box::new(FullIndex::new())));
585 assert_eq!(property_store.index_new_entity_fns.len(), 1);
586
587 property_store.install_property_index::<Age>(None);
588 assert_eq!(property_store.index_new_entity_fns.len(), 0);
589
590 property_store.install_property_index::<Age>(Some(Box::new(ValueCountIndex::new())));
591 property_store.install_property_index::<Vaccinated>(Some(Box::new(FullIndex::new())));
592 property_store.install_property_index::<Age>(None);
593
594 assert_eq!(property_store.index_new_entity_fns.len(), 1);
595 assert_eq!(property_store.index_new_entity_fns[0].0, Vaccinated::id());
596 }
597
598 context.add_entity(with!(Person, Age(10))).unwrap();
599 let property_store = context.entity_store.get_property_store::<Person>();
600 assert_eq!(
601 property_store.get::<Age>().index_type(),
602 PropertyIndexType::Unindexed
603 );
604 assert_eq!(
605 property_store.get_index_count_for_query_parts(
606 Vaccinated::id(),
607 &[&Vaccinated(false) as &dyn Any],
608 ),
609 IndexCountResult::Count(1),
610 );
611 }
612
613 #[test]
614 #[should_panic(
615 expected = "Ixa internal error: indexed property is missing its index_new_entity dispatcher"
616 )]
617 fn removing_index_without_dispatcher_panics() {
618 let mut property_store = PropertyStore::<Person>::new();
619 property_store.install_property_index::<Age>(Some(Box::new(FullIndex::new())));
620 property_store.index_new_entity_fns.clear();
621
622 property_store.install_property_index::<Age>(None);
623 }
624
625 #[test]
626 #[should_panic(
627 expected = "Ixa internal error: unindexed property unexpectedly has an index_new_entity dispatcher"
628 )]
629 fn dispatcher_without_index_panics() {
630 let mut property_store = PropertyStore::<Person>::new();
631 property_store.install_property_index::<Age>(Some(Box::new(FullIndex::new())));
632 property_store.get_mut::<Age>().index = None;
633
634 property_store.install_property_index::<Age>(None);
635 }
636
637 #[test]
638 fn failed_replacement_keeps_old_index_and_dispatcher() {
639 let mut context = Context::new();
640 let ordinary = context
641 .add_entity(with!(Person, Age(10), PanicDependency(10)))
642 .unwrap();
643 let sentinel = context
644 .add_entity(with!(Person, Age(20), PanicDependency(255)))
645 .unwrap();
646
647 let mut old_index = ValueCountIndex::<Person, PanickingDerived>::new();
648 old_index.add_entity(&PanickingDerived(10), ordinary);
649 old_index.add_entity(&PanickingDerived(255), sentinel);
650 context
651 .entity_store
652 .get_property_store_mut::<Person>()
653 .install_property_index::<PanickingDerived>(Some(Box::new(old_index)));
654
655 let (old_type, old_dispatcher_count, old_dispatcher_property_id) = {
656 let property_store = context.entity_store.get_property_store::<Person>();
657 (
658 property_store.get::<PanickingDerived>().index_type(),
659 property_store.index_new_entity_fns.len(),
660 property_store.index_new_entity_fns[0].0,
661 )
662 };
663 assert_eq!(old_type, PropertyIndexType::ValueCountIndex);
664 assert_eq!(
665 context.query_entity_count(with!(Person, PanickingDerived(10))),
666 1
667 );
668 assert_eq!(
669 context.query_entity_count(with!(Person, PanickingDerived(255))),
670 1
671 );
672
673 let result = catch_unwind(AssertUnwindSafe(|| {
674 context.index_property::<Person, PanickingDerived>();
675 }));
676 assert!(result.is_err());
677
678 let property_store = context.entity_store.get_property_store::<Person>();
679 assert_eq!(
680 property_store.get::<PanickingDerived>().index_type(),
681 old_type
682 );
683 assert_eq!(
684 property_store.index_new_entity_fns.len(),
685 old_dispatcher_count
686 );
687 assert_eq!(
688 property_store.index_new_entity_fns[0].0,
689 old_dispatcher_property_id
690 );
691 assert_eq!(
692 context.query_entity_count(with!(Person, PanickingDerived(10))),
693 1
694 );
695 assert_eq!(
696 context.query_entity_count(with!(Person, PanickingDerived(255))),
697 1
698 );
699
700 context
701 .add_entity(with!(Person, Age(30), PanicDependency(10)))
702 .unwrap();
703 assert_eq!(
704 context.query_entity_count(with!(Person, PanickingDerived(10))),
705 2
706 );
707 }
708
709 #[test]
710 fn test_get_property_store() {
711 let mut property_store = PropertyStore::new();
712
713 {
714 let ages: &mut PropertyValueStoreCore<_, Age> = property_store.get_mut();
715 ages.set(EntityId::<Person>::new(0), Age(12));
716 ages.set(EntityId::<Person>::new(1), Age(33));
717 ages.set(EntityId::<Person>::new(2), Age(44));
718
719 let infection_statuses: &mut PropertyValueStoreCore<_, InfectionStatus> =
720 property_store.get_mut();
721 infection_statuses.set(EntityId::<Person>::new(0), InfectionStatus::Susceptible);
722 infection_statuses.set(EntityId::<Person>::new(1), InfectionStatus::Susceptible);
723 infection_statuses.set(EntityId::<Person>::new(2), InfectionStatus::Infected);
724
725 let vaccine_status: &mut PropertyValueStoreCore<_, Vaccinated> =
726 property_store.get_mut();
727 vaccine_status.set(EntityId::<Person>::new(0), Vaccinated(true));
728 vaccine_status.set(EntityId::<Person>::new(1), Vaccinated(false));
729 vaccine_status.set(EntityId::<Person>::new(2), Vaccinated(true));
730 }
731
732 {
734 let ages: &PropertyValueStoreCore<_, Age> = property_store.get();
735 assert_eq!(ages.get(EntityId::<Person>::new(0)), Age(12));
736 assert_eq!(ages.get(EntityId::<Person>::new(1)), Age(33));
737 assert_eq!(ages.get(EntityId::<Person>::new(2)), Age(44));
738
739 let infection_statuses: &PropertyValueStoreCore<_, InfectionStatus> =
740 property_store.get();
741 assert_eq!(
742 infection_statuses.get(EntityId::<Person>::new(0)),
743 InfectionStatus::Susceptible
744 );
745 assert_eq!(
746 infection_statuses.get(EntityId::<Person>::new(1)),
747 InfectionStatus::Susceptible
748 );
749 assert_eq!(
750 infection_statuses.get(EntityId::<Person>::new(2)),
751 InfectionStatus::Infected
752 );
753
754 let vaccine_status: &PropertyValueStoreCore<_, Vaccinated> = property_store.get();
755 assert_eq!(
756 vaccine_status.get(EntityId::<Person>::new(0)),
757 Vaccinated(true)
758 );
759 assert_eq!(
760 vaccine_status.get(EntityId::<Person>::new(1)),
761 Vaccinated(false)
762 );
763 assert_eq!(
764 vaccine_status.get(EntityId::<Person>::new(2)),
765 Vaccinated(true)
766 );
767 }
768 }
769
770 #[test]
771 fn test_index_query_results_for_property_store() {
772 let mut context = Context::new();
773 context.index_property::<Person, Age>();
774
775 let existing_value = Age(12);
776 let missing_value = Age(99);
777 let existing_query_parts = [&existing_value as &dyn Any];
778 let missing_query_parts = [&missing_value as &dyn Any];
779
780 let _ = context.add_entity(with!(Person, existing_value)).unwrap();
781 let _ = context.add_entity(with!(Person, existing_value)).unwrap();
782
783 let property_store = context.entity_store.get_property_store::<Person>();
784
785 assert_eq!(
787 property_store.get_index_count_for_query_parts(Age::id(), &missing_query_parts,),
788 IndexCountResult::Count(0)
789 );
790 assert_eq!(
791 property_store.get_index_count_for_query_parts(Age::id(), &existing_query_parts,),
792 IndexCountResult::Count(2)
793 );
794
795 assert!(matches!(
797 property_store.get_index_set_for_query_parts(Age::id(), &missing_query_parts,),
798 IndexSetResult::Empty
799 ));
800 assert!(matches!(
801 property_store.get_index_set_for_query_parts(
802 Age::id(),
803 &existing_query_parts,
804 ),
805 IndexSetResult::Set(set) if set.len() == 2
806 ));
807 }
808
809 #[test]
810 fn test_index_query_results_for_property_store_value_count_index() {
811 let mut context = Context::new();
812 context.index_property_counts::<Person, Age>();
813
814 let existing_value = Age(12);
815 let missing_value = Age(99);
816 let existing_query_parts = [&existing_value as &dyn Any];
817 let missing_query_parts = [&missing_value as &dyn Any];
818
819 let _ = context.add_entity(with!(Person, existing_value)).unwrap();
820 let _ = context.add_entity(with!(Person, existing_value)).unwrap();
821
822 let property_store = context.entity_store.get_property_store::<Person>();
823
824 assert_eq!(
826 property_store.get_index_count_for_query_parts(Age::id(), &missing_query_parts,),
827 IndexCountResult::Count(0)
828 );
829 assert_eq!(
830 property_store.get_index_count_for_query_parts(Age::id(), &existing_query_parts,),
831 IndexCountResult::Count(2)
832 );
833
834 assert!(matches!(
836 property_store.get_index_set_for_query_parts(Age::id(), &missing_query_parts,),
837 IndexSetResult::Unsupported
838 ));
839 assert!(matches!(
840 property_store.get_index_set_for_query_parts(Age::id(), &existing_query_parts,),
841 IndexSetResult::Unsupported
842 ));
843 }
844
845 #[test]
846 fn test_index_query_results_for_property_store_unindexed() {
847 let mut context = Context::new();
848 let existing_value = Age(12);
849 let missing_value = Age(99);
850 let existing_query_parts = [&existing_value as &dyn Any];
851 let missing_query_parts = [&missing_value as &dyn Any];
852
853 let _ = context.add_entity(with!(Person, existing_value)).unwrap();
854 let _ = context.add_entity(with!(Person, existing_value)).unwrap();
855
856 let property_store = context.entity_store.get_property_store::<Person>();
857
858 assert_eq!(
860 property_store.get_index_count_for_query_parts(Age::id(), &missing_query_parts,),
861 IndexCountResult::Unsupported
862 );
863 assert_eq!(
864 property_store.get_index_count_for_query_parts(Age::id(), &existing_query_parts,),
865 IndexCountResult::Unsupported
866 );
867
868 assert!(matches!(
870 property_store.get_index_set_for_query_parts(Age::id(), &missing_query_parts,),
871 IndexSetResult::Unsupported
872 ));
873 assert!(matches!(
874 property_store.get_index_set_for_query_parts(Age::id(), &existing_query_parts,),
875 IndexSetResult::Unsupported
876 ));
877 }
878}