1#[macro_export]
211macro_rules! define_property {
212 (
231 struct $name:ident ( $visibility:vis Option<$inner_ty:ty> ),
232 $entity:ident,
233 impl_eq_hash = $impl_eq_hash:ident
234 $(, $($extra:tt)*)?
235 ) => {
236 $crate::define_property!(
237 @apply_property_decoration $impl_eq_hash,
238 pub struct $name(pub Option<$inner_ty>);,
239 $name
240 );
241 $crate::impl_property!(@with_option_display_default $name, $entity $(, $($extra)*)?);
242 };
243 (
244 struct $name:ident ( $visibility:vis Option<$inner_ty:ty> ),
245 $entity:ident
246 $(, $($extra:tt)*)?
247 ) => {
248 $crate::define_property!(
249 @apply_property_decoration ,
250 pub struct $name(pub Option<$inner_ty>);,
251 $name
252 );
253 $crate::impl_property!(@with_option_display_default $name, $entity $(, $($extra)*)?);
254 };
255
256 (
257 struct $name:ident ( $($visibility:vis $field_ty:ty),* $(,)? ),
258 $entity:ident,
259 impl_eq_hash = $impl_eq_hash:ident
260 $(, $($extra:tt)*)?
261 ) => {
262 $crate::define_property!(
263 @apply_property_decoration $impl_eq_hash,
264 pub struct $name($(pub $field_ty),*);,
265 $name
266 );
267 $crate::impl_property!($name, $entity $(, $($extra)*)?);
268 };
269 (
270 struct $name:ident ( $($visibility:vis $field_ty:ty),* $(,)? ),
271 $entity:ident
272 $(, $($extra:tt)*)?
273 ) => {
274 $crate::define_property!(
275 @apply_property_decoration ,
276 pub struct $name($(pub $field_ty),*);,
277 $name
278 );
279 $crate::impl_property!($name, $entity $(, $($extra)*)?);
280 };
281
282 (
283 struct $name:ident { $($visibility:vis $field_name:ident : $field_ty:ty),* $(,)? },
284 $entity:ident,
285 impl_eq_hash = $impl_eq_hash:ident
286 $(, $($extra:tt)*)?
287 ) => {
288 $crate::define_property!(
289 @apply_property_decoration $impl_eq_hash,
290 pub struct $name { $(pub $field_name : $field_ty),* },
291 $name
292 );
293 $crate::impl_property!($name, $entity $(, $($extra)*)?);
294 };
295 (
296 struct $name:ident { $($visibility:vis $field_name:ident : $field_ty:ty),* $(,)? },
297 $entity:ident
298 $(, $($extra:tt)*)?
299 ) => {
300 $crate::define_property!(
301 @apply_property_decoration ,
302 pub struct $name { $(pub $field_name : $field_ty),* },
303 $name
304 );
305 $crate::impl_property!($name, $entity $(, $($extra)*)?);
306 };
307
308 (
309 enum $name:ident {
310 $($variant:ident),* $(,)?
311 },
312 $entity:ident,
313 impl_eq_hash = $impl_eq_hash:ident
314 $(, $($extra:tt)*)?
315 ) => {
316 $crate::define_property!(
317 @apply_property_decoration $impl_eq_hash,
318 pub enum $name { $($variant),* },
319 $name
320 );
321 $crate::impl_property!($name, $entity $(, $($extra)*)?);
322 };
323 (
324 enum $name:ident {
325 $($variant:ident),* $(,)?
326 },
327 $entity:ident
328 $(, $($extra:tt)*)?
329 ) => {
330 $crate::define_property!(
331 @apply_property_decoration ,
332 pub enum $name { $($variant),* },
333 $name
334 );
335 $crate::impl_property!($name, $entity $(, $($extra)*)?);
336 };
337
338 (@apply_property_decoration , $item:item, $name:ident) => {
341 #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, serde::Serialize, serde::Deserialize)]
342 $item
343 };
344 (@apply_property_decoration Eq, $item:item, $name:ident) => {
345 #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, $crate::rkyv::Archive, $crate::rkyv::Serialize)]
346 #[rkyv(crate = $crate::rkyv)]
347 $item
348 $crate::define_property!(@apply_property_decoration_eq_impl $name);
349 };
350 (@apply_property_decoration Hash, $item:item, $name:ident) => {
351 #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, $crate::rkyv::Archive, $crate::rkyv::Serialize)]
352 #[rkyv(crate = $crate::rkyv)]
353 $item
354 $crate::define_property!(@apply_property_decoration_hash_impl $name);
355 };
356 (@apply_property_decoration both, $item:item, $name:ident) => {
357 #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, $crate::rkyv::Archive, $crate::rkyv::Serialize)]
358 #[rkyv(crate = $crate::rkyv)]
359 $item
360 $crate::define_property!(@apply_property_decoration_eq_impl $name);
361 $crate::define_property!(@apply_property_decoration_hash_impl $name);
362 };
363 (@apply_property_decoration neither, $item:item, $name:ident) => {
364 #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
365 $item
366 };
367 (@apply_property_decoration $mode:ident, $item:item, $name:ident) => {
368 compile_error!("`impl_eq_hash` must be one of `Eq`, `Hash`, `both`, or `neither`");
369 };
370
371 (@apply_property_decoration_eq_impl $name:ident) => {
372 impl core::cmp::PartialEq for $name {
373 fn eq(&self, other: &Self) -> bool {
374 const N: usize = core::mem::size_of::<<$name as $crate::rkyv::Archive>::Archived>();
375
376 let left = $crate::rkyv::api::high::to_bytes_in::<_, $crate::rkyv::rancor::Error>(
377 self,
378 $crate::hashing::EqualityBufferWriter::<N>::new(),
379 )
380 .expect("serializing left value for equality comparison failed");
381
382 let right = $crate::rkyv::api::high::to_bytes_in::<_, $crate::rkyv::rancor::Error>(
383 other,
384 $crate::hashing::EqualityBufferWriter::<N>::new(),
385 )
386 .expect("serializing right value for equality comparison failed");
387
388 left.as_written() == right.as_written()
389 }
390 }
391
392 impl core::cmp::Eq for $name {}
393 };
394 (@apply_property_decoration_hash_impl $name:ident) => {
395 impl core::hash::Hash for $name {
396 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
397 $crate::rkyv::api::high::to_bytes_in::<_, $crate::rkyv::rancor::Error>(
398 self,
399 $crate::hashing::HasherWriter::new(state),
400 )
401 .expect("serialization failed while hashing");
402 }
403 }
404 };
405
406}
407
408#[macro_export]
469macro_rules! impl_property {
470 (
471 $property:ident,
472 $entity:ident
473 $(, compute_derived_fn = $compute_derived_fn:expr)?
474 $(, default_const = $default_const:expr)?
475 $(, canonical_value = $canonical_value:ty)?
476 $(, make_canonical = $make_canonical:expr)?
477 $(, make_uncanonical = $make_uncanonical:expr)?
478 $(, index_id_fn = $index_id_fn:expr)?
479 $(, collect_deps_fn = $collect_deps_fn:expr)?
480 $(, display_impl = $display_impl:expr)?
481 $(, ctor_registration = $ctor_registration:expr)?
482 ) => {
483 $crate::impl_property!(@assert_not_both $($compute_derived_fn)? ; $($default_const)?);
485
486 $crate::impl_property!(
487 @__impl_property_common
488 $property,
489 $entity,
490
491 $crate::impl_property!(@unwrap_or_ty $($canonical_value)?, $property),
493
494 $crate::impl_property!(@select_initialization_kind $($compute_derived_fn)? ; $($default_const)?),
496
497 $crate::impl_property!(
499 @unwrap_or
500 $($compute_derived_fn)?,
501 |_, _| panic!("property {} is not derived", stringify!($property))
502 ),
503
504 $crate::impl_property!(
506 @unwrap_or
507 $($default_const)?,
508 panic!("property {} has no default value", stringify!($property))
509 ),
510
511 $crate::impl_property!(@unwrap_or $($make_canonical)?, std::convert::identity),
513
514 $crate::impl_property!(@unwrap_or $($make_uncanonical)?, std::convert::identity),
516
517 [&'a dyn std::any::Any; 1],
519
520 {
522 |parts: &[&dyn std::any::Any]| -> Option<<$property as $crate::entity::property::Property<$entity>>::CanonicalValue> {
523 let [part] = parts else {
524 return None;
525 };
526 part.downcast_ref::<$property>()
527 .copied()
528 .map(<$property as $crate::entity::property::Property<$entity>>::make_canonical)
529 }
530 },
531
532 {
534 fn default_query_parts_for_value<'a>(value: &'a $property) -> [&'a dyn std::any::Any; 1] {
535 [value as &'a dyn std::any::Any]
536 }
537
538 default_query_parts_for_value
539 },
540
541 $crate::impl_property!(@unwrap_or $($display_impl)?, |v| format!("{v:?}")),
543
544 $crate::impl_property!(@unwrap_or $($index_id_fn)?, {
546 <Self as $crate::entity::property::Property<$entity>>::id()
547 }),
548
549 $crate::impl_property!(
551 @unwrap_or
552 $($collect_deps_fn)?,
553 |_| {}
554 ),
555
556 $crate::impl_property!(@unwrap_or $($ctor_registration)?, {
558 $crate::entity::property_store::add_to_property_registry::<$entity, $property>();
559 }),
560 );
561 };
562
563 (
564 @with_option_display_default
565 $property:ident,
566 $entity:ident
567 $(, compute_derived_fn = $compute_derived_fn:expr)?
568 $(, default_const = $default_const:expr)?
569 $(, canonical_value = $canonical_value:ty)?
570 $(, make_canonical = $make_canonical:expr)?
571 $(, make_uncanonical = $make_uncanonical:expr)?
572 $(, index_id_fn = $index_id_fn:expr)?
573 $(, collect_deps_fn = $collect_deps_fn:expr)?
574 $(, display_impl = $display_impl:expr)?
575 $(, ctor_registration = $ctor_registration:expr)?
576 ) => {
577 $crate::impl_property!(
578 $property,
579 $entity
580 $(, compute_derived_fn = $compute_derived_fn)?
581 $(, default_const = $default_const)?
582 $(, canonical_value = $canonical_value)?
583 $(, make_canonical = $make_canonical)?
584 $(, make_uncanonical = $make_uncanonical)?
585 $(, index_id_fn = $index_id_fn)?
586 $(, collect_deps_fn = $collect_deps_fn)?
587 , display_impl = $crate::impl_property!(@unwrap_or $($display_impl)?, |value: &Self| {
588 match value.0 {
589 Some(v) => format!("{:?}", v),
590 None => "None".to_string(),
591 }
592 })
593 $(, ctor_registration = $ctor_registration)?
594 );
595 };
596
597 (
598 @multi_property
599 $property:ident,
600 $entity:ident,
601 ( $($dependency:ident),+ )
602 $(, compute_derived_fn = $compute_derived_fn:expr)?
603 $(, default_const = $default_const:expr)?
604 $(, canonical_value = $canonical_value:ty)?
605 $(, make_canonical = $make_canonical:expr)?
606 $(, make_uncanonical = $make_uncanonical:expr)?
607 $(, index_id_fn = $index_id_fn:expr)?
608 $(, collect_deps_fn = $collect_deps_fn:expr)?
609 $(, display_impl = $display_impl:expr)?
610 $(, ctor_registration = $ctor_registration:expr)?
611 ) => {
612 $crate::impl_property!(@assert_not_both $($compute_derived_fn)? ; $($default_const)?);
613
614 $crate::impl_property!(
615 @__impl_property_common
616 $property,
617 $entity,
618 $crate::impl_property!(@unwrap_or_ty $($canonical_value)?, $property),
619 $crate::impl_property!(@select_initialization_kind $($compute_derived_fn)? ; $($default_const)?),
620 $crate::impl_property!(
621 @unwrap_or
622 $($compute_derived_fn)?,
623 |_, _| panic!("property {} is not derived", stringify!($property))
624 ),
625 $crate::impl_property!(
626 @unwrap_or
627 $($default_const)?,
628 panic!("property {} has no default value", stringify!($property))
629 ),
630 $crate::impl_property!(@unwrap_or $($make_canonical)?, std::convert::identity),
631 $crate::impl_property!(@unwrap_or $($make_uncanonical)?, std::convert::identity),
632 [&'a dyn std::any::Any; $crate::impl_property!(@count_tts $($dependency),+)],
633 $crate::canonical_from_sorted_query_parts_closure!(( $($dependency),+ )),
634 {
635 $crate::paste::paste! {
636 fn multi_property_query_parts_for_value<'a>(
637 value: &'a $property,
638 ) -> [&'a dyn std::any::Any; $crate::impl_property!(@count_tts $($dependency),+)] {
639 let keys = [
640 $(
641 <$dependency as $crate::entity::property::Property<$entity>>::name(),
642 )+
643 ];
644 let ( $( [<_ $dependency:lower>] ),+ ) = value;
645 let mut parts = [
646 $(
647 [<_ $dependency:lower>] as &'a dyn std::any::Any,
648 )+
649 ];
650 $crate::entity::multi_property::static_reorder_by_keys(&keys, &mut parts);
651 parts
652 }
653
654 multi_property_query_parts_for_value
655 }
656 },
657 $crate::impl_property!(@unwrap_or $($display_impl)?, |v| format!("{v:?}")),
658 $crate::impl_property!(@unwrap_or $($index_id_fn)?, {
659 <Self as $crate::entity::property::Property<$entity>>::id()
660 }),
661 $crate::impl_property!(@unwrap_or $($collect_deps_fn)?, |_| {}),
662 $crate::impl_property!(@unwrap_or $($ctor_registration)?, {
663 $crate::entity::property_store::add_to_property_registry::<$entity, $property>();
664 }),
665 );
666 };
667
668 (@assert_not_both $compute_derived_fn:expr ; $default_const:expr) => {
670 compile_error!(
671 "impl_property!: `compute_derived_fn = ...` (derived property) and `default_const = ...` \
672 (non-derived property default constant) are mutually exclusive. Remove one of them."
673 );
674 };
675 (@assert_not_both $compute_derived_fn:expr ; ) => {};
676 (@assert_not_both ; $default_const:expr) => {};
677 (@assert_not_both ; ) => {};
678
679 (@select_initialization_kind $compute_derived_fn:expr ; $default_const:expr) => {
681 compile_error!(
684 "impl_property!: cannot select initialization kind because both `compute_derived_fn` \
685 and `default_const` are present"
686 )
687 };
688 (@select_initialization_kind $compute_derived_fn:expr ; ) => {
689 $crate::entity::property::PropertyInitializationKind::Derived
690 };
691 (@select_initialization_kind ; $default_const:expr) => {
692 $crate::entity::property::PropertyInitializationKind::Constant
693 };
694 (@select_initialization_kind ; ) => {
695 $crate::entity::property::PropertyInitializationKind::Explicit
696 };
697
698 (@unwrap_or $value:expr, $_default:expr) => { $value };
700 (@unwrap_or, $default:expr) => { $default };
701
702 (@unwrap_or_ty $ty:ty, $_default:ty) => { $ty };
703 (@unwrap_or_ty, $default:ty) => { $default };
704
705 (@replace_with_unit $_tt:tt) => { () };
706 (@count_tts $($tt:tt),* $(,)?) => {
707 <[()]>::len(&[$($crate::impl_property!(@replace_with_unit $tt)),*])
708 };
709
710 (
712 @__impl_property_common
713 $property:ident, $entity:ident, $canonical_value:ty, $initialization_kind:expr, $compute_derived_fn:expr, $default_const:expr, $make_canonical:expr, $make_uncanonical:expr, $query_parts_type:ty,
722 $canonical_from_sorted_query_parts_fn:expr,
723 $query_parts_for_value_fn:expr,
724 $display_impl:expr, $index_id_fn:expr, $collect_deps_fn:expr, $ctor_registration:expr, ) => {
729 impl $crate::entity::property::Property<$entity> for $property {
730 type CanonicalValue = $canonical_value;
731 type QueryParts<'a> = $query_parts_type where Self: 'a;
732
733 const NAME: &'static str = stringify!($property);
734
735 fn initialization_kind() -> $crate::entity::property::PropertyInitializationKind {
736 $initialization_kind
737 }
738
739 fn compute_derived(
740 _context: &$crate::Context,
741 _entity_id: $crate::entity::EntityId<$entity>,
742 ) -> Self {
743 ($compute_derived_fn)(_context, _entity_id)
744 }
745
746 fn default_const() -> Self {
747 $default_const
748 }
749
750 fn make_canonical(self) -> Self::CanonicalValue {
751 ($make_canonical)(self)
752 }
753
754 fn make_uncanonical(value: Self::CanonicalValue) -> Self {
755 ($make_uncanonical)(value)
756 }
757
758 fn canonical_from_sorted_query_parts(
759 parts: &[&dyn std::any::Any],
760 ) -> Option<Self::CanonicalValue> {
761 ($canonical_from_sorted_query_parts_fn)(parts)
762 }
763
764 fn query_parts_for_value(value: &Self) -> Self::QueryParts<'_> {
765 ($query_parts_for_value_fn)(value)
766 }
767
768 fn get_display(&self) -> String {
769 ($display_impl)(self)
770 }
771
772 fn id() -> usize {
773 static INDEX: std::sync::atomic::AtomicUsize =
777 std::sync::atomic::AtomicUsize::new(usize::MAX);
778
779 let index = INDEX.load(std::sync::atomic::Ordering::Relaxed);
781 if index != usize::MAX {
782 return index;
783 }
784
785 $crate::entity::property_store::initialize_property_id::<$entity>(&INDEX)
787 }
788
789 fn index_id() -> usize {
790 $index_id_fn
791 }
792
793 fn collect_non_derived_dependencies(result: &mut $crate::HashSet<usize>) {
794 ($collect_deps_fn)(result)
795 }
796 }
797
798 $crate::paste::paste! {
799 $crate::ctor::declarative::ctor!{
800 #[ctor(unsafe)]
801 fn [<_register_property_ $entity:snake _ $property:snake>]() {
802 $ctor_registration
803 }
804 }
805 }
806 };
807}
808
809#[macro_export]
823macro_rules! define_derived_property {
824 (
833 struct $name:ident ( $visibility:vis Option<$inner_ty:ty> ),
834 $entity:ident,
835 [$($dependency:ident),*]
836 $(, [$($global_dependency:ident),*])?,
837 |$($param:ident),+| $derive_fn:expr,
838 impl_eq_hash = $impl_eq_hash:ident
839 $(, $($extra:tt)+)*
840 ) => {
841 $crate::define_property!(
842 @apply_property_decoration
843 $impl_eq_hash,
844 pub struct $name(pub Option<$inner_ty>);,
845 $name
846 );
847
848 $crate::impl_derived_property!(
849 @with_option_display_default
850 $name,
851 $entity,
852 [$($dependency),*],
853 [$($($global_dependency),*)?],
854 |$($param),+| $derive_fn
855 $(, $($extra)+)*
856 );
857 };
858 (
859 struct $name:ident ( $visibility:vis Option<$inner_ty:ty> ),
860 $entity:ident,
861 [$($dependency:ident),*]
862 $(, [$($global_dependency:ident),*])?,
863 |$($param:ident),+| $derive_fn:expr
864 $(, $($extra:tt)+)*
865 ) => {
866 $crate::define_property!(
867 @apply_property_decoration
868 ,
869 pub struct $name(pub Option<$inner_ty>);,
870 $name
871 );
872
873 $crate::impl_derived_property!(
874 @with_option_display_default
875 $name,
876 $entity,
877 [$($dependency),*],
878 [$($($global_dependency),*)?],
879 |$($param),+| $derive_fn
880 $(, $($extra)+)*
881 );
882 };
883
884 (
886 struct $name:ident ( $($visibility:vis $field_ty:ty),* $(,)? ),
887 $entity:ident,
888 [$($dependency:ident),*]
889 $(, [$($global_dependency:ident),*])?,
890 |$($param:ident),+| $derive_fn:expr,
891 impl_eq_hash = $impl_eq_hash:ident
892 $(, $($extra:tt)+)*
893 ) => {
894 $crate::define_property!(
895 @apply_property_decoration
896 $impl_eq_hash,
897 pub struct $name( $(pub $field_ty),* );,
898 $name
899 );
900
901 $crate::impl_derived_property!(
902 $name,
903 $entity,
904 [$($dependency),*],
905 [$($($global_dependency),*)?],
906 |$($param),+| $derive_fn
907 $(, $($extra)+)*
908 );
909 };
910 (
911 struct $name:ident ( $($visibility:vis $field_ty:ty),* $(,)? ),
912 $entity:ident,
913 [$($dependency:ident),*]
914 $(, [$($global_dependency:ident),*])?,
915 |$($param:ident),+| $derive_fn:expr
916 $(, $($extra:tt)+)*
917 ) => {
918 $crate::define_property!(
919 @apply_property_decoration
920 ,
921 pub struct $name( $(pub $field_ty),* );,
922 $name
923 );
924
925 $crate::impl_derived_property!(
926 $name,
927 $entity,
928 [$($dependency),*],
929 [$($($global_dependency),*)?],
930 |$($param),+| $derive_fn
931 $(, $($extra)+)*
932 );
933 };
934
935 (
937 struct $name:ident { $($visibility:vis $field_name:ident : $field_ty:ty),* $(,)? },
938 $entity:ident,
939 [$($dependency:ident),*]
940 $(, [$($global_dependency:ident),*])?,
941 |$($param:ident),+| $derive_fn:expr,
942 impl_eq_hash = $impl_eq_hash:ident
943 $(, $($extra:tt)+)*
944 ) => {
945 $crate::define_property!(
946 @apply_property_decoration
947 $impl_eq_hash,
948 pub struct $name { $($visibility $field_name : $field_ty),* },
949 $name
950 );
951
952 $crate::impl_derived_property!(
953 $name,
954 $entity,
955 [$($dependency),*],
956 [$($($global_dependency),*)?],
957 |$($param),+| $derive_fn
958 $(, $($extra)+)*
959 );
960 };
961 (
962 struct $name:ident { $($visibility:vis $field_name:ident : $field_ty:ty),* $(,)? },
963 $entity:ident,
964 [$($dependency:ident),*]
965 $(, [$($global_dependency:ident),*])?,
966 |$($param:ident),+| $derive_fn:expr
967 $(, $($extra:tt)+)*
968 ) => {
969 $crate::define_property!(
970 @apply_property_decoration
971 ,
972 pub struct $name { $($visibility $field_name : $field_ty),* },
973 $name
974 );
975
976 $crate::impl_derived_property!(
977 $name,
978 $entity,
979 [$($dependency),*],
980 [$($($global_dependency),*)?],
981 |$($param),+| $derive_fn
982 $(, $($extra)+)*
983 );
984 };
985
986 (
988 enum $name:ident {
989 $($variant:ident),* $(,)?
990 },
991 $entity:ident,
992 [$($dependency:ident),*]
993 $(, [$($global_dependency:ident),*])?,
994 |$($param:ident),+| $derive_fn:expr,
995 impl_eq_hash = $impl_eq_hash:ident
996 $(, $($extra:tt)+)*
997 ) => {
998 $crate::define_property!(
999 @apply_property_decoration
1000 $impl_eq_hash,
1001 pub enum $name {
1002 $($variant),*
1003 },
1004 $name
1005 );
1006
1007 $crate::impl_derived_property!(
1008 $name,
1009 $entity,
1010 [$($dependency),*],
1011 [$($($global_dependency),*)?],
1012 |$($param),+| $derive_fn
1013 $(, $($extra)+)*
1014 );
1015 };
1016 (
1017 enum $name:ident {
1018 $($variant:ident),* $(,)?
1019 },
1020 $entity:ident,
1021 [$($dependency:ident),*]
1022 $(, [$($global_dependency:ident),*])?,
1023 |$($param:ident),+| $derive_fn:expr
1024 $(, $($extra:tt)+)*
1025 ) => {
1026 $crate::define_property!(
1027 @apply_property_decoration
1028 ,
1029 pub enum $name {
1030 $($variant),*
1031 },
1032 $name
1033 );
1034
1035 $crate::impl_derived_property!(
1036 $name,
1037 $entity,
1038 [$($dependency),*],
1039 [$($($global_dependency),*)?],
1040 |$($param),+| $derive_fn
1041 $(, $($extra)+)*
1042 );
1043 };
1044}
1045
1046#[macro_export]
1052macro_rules! impl_derived_property {
1053 (
1054 $name:ident,
1055 $entity:ident,
1056 [$($dependency:ident),*]
1057 $(, [$($global_dependency:ident),*])?,
1058 |$($param:ident),+| $derive_fn:expr
1059 $(, $($extra:tt)+)*
1060 ) => {
1061 $crate::impl_property!(
1062 $name,
1063 $entity,
1064 compute_derived_fn = $crate::impl_derived_property!(
1065 @construct_compute_fn
1066 $entity,
1067 [$($dependency),*],
1068 [$($($global_dependency),*)?],
1069 |$($param),+| $derive_fn
1070 ),
1071 collect_deps_fn = | deps: &mut $crate::HashSet<usize> | {
1072 $(
1073 if <$dependency as $crate::entity::property::Property<$entity>>::is_derived() {
1074 <$dependency as $crate::entity::property::Property<$entity>>::collect_non_derived_dependencies(deps);
1075 } else {
1076 deps.insert(<$dependency as $crate::entity::property::Property<$entity>>::id());
1077 }
1078 )*
1079 }
1080 $(, $($extra)+)*
1081 );
1082 };
1083
1084 (
1086 @construct_compute_fn
1087 $entity:ident,
1088 [$($dependency:ident),*],
1089 [$($global_dependency:ident),*],
1090 |$($param:ident),+| $derive_fn:expr
1091 ) => {
1092 |context: &$crate::Context, entity_id| {
1093 #[allow(unused_imports)]
1094 use $crate::global_properties::ContextGlobalPropertiesExt;
1095 #[allow(unused_parens)]
1096 let ($($param,)*) = (
1097 $(context.get_property::<$entity, $dependency>(entity_id)),*,
1098 $(
1099 context.get_global_property_value($global_dependency)
1100 .expect(&format!("Global property {} not initialized", stringify!($global_dependency)))
1101 ),*
1102 );
1103 $derive_fn
1104 }
1105 };
1106
1107 (@unwrap_or $value:expr, $_default:expr) => { $value };
1108 (@unwrap_or, $default:expr) => { $default };
1109
1110 (
1111 @with_option_display_default
1112 $name:ident,
1113 $entity:ident,
1114 [$($dependency:ident),*],
1115 [$($global_dependency:ident),*],
1116 |$($param:ident),+| $derive_fn:expr
1117 $(, default_const = $default_const:expr)?
1118 $(, display_impl = $display_impl:expr)?
1119 $(, canonical_value = $canonical_value:ty)?
1120 $(, make_canonical = $make_canonical:expr)?
1121 $(, make_uncanonical = $make_uncanonical:expr)?
1122 $(, index_id_fn = $index_id_fn:expr)?
1123 $(, collect_deps_fn = $collect_deps_fn:expr)?
1124 $(, ctor_registration = $ctor_registration:expr)?
1125 ) => {
1126 $crate::impl_derived_property!(
1127 $name,
1128 $entity,
1129 [$($dependency),*],
1130 [$($global_dependency),*],
1131 |$($param),+| $derive_fn
1132 $(, default_const = $default_const)?
1133 , display_impl = $crate::impl_derived_property!(@unwrap_or $($display_impl)?, |value: &$name| {
1134 match value.0 {
1135 Some(v) => format!("{:?}", v),
1136 None => "None".to_string(),
1137 }
1138 })
1139 $(, canonical_value = $canonical_value)?
1140 $(, make_canonical = $make_canonical)?
1141 $(, make_uncanonical = $make_uncanonical)?
1142 $(, index_id_fn = $index_id_fn)?
1143 $(, collect_deps_fn = $collect_deps_fn)?
1144 $(, ctor_registration = $ctor_registration)?
1145 );
1146 };
1147
1148}
1149
1150#[macro_export]
1169macro_rules! define_multi_property {
1170 (
1171 ( $($dependency:ident),+ ),
1172 $entity:ident
1173 ) => {
1174 $crate::paste::paste! {
1175 type [<$($dependency)*>] = ( $($dependency),+ );
1176
1177 $(
1179 const _: () = assert!(
1180 $crate::entity::property::const_str_eq(
1181 stringify!($dependency),
1182 <$dependency as $crate::entity::property::Property<$entity>>::NAME,
1183 ),
1184 concat!(
1185 "define_multi_property!: `",
1186 stringify!($dependency),
1187 "` is a type alias; use the underlying property type (see issue #843)."
1188 ),
1189 );
1190 )+
1191
1192 $crate::impl_property!(
1193 @multi_property
1194 [<$($dependency)*>],
1195 $entity,
1196 ( $($dependency),+ ),
1197 compute_derived_fn = |context: &$crate::Context, entity_id: $crate::entity::EntityId<$entity>| {
1198 (
1199 $(context.get_property::<$entity, $dependency>(entity_id)),+
1200 )
1201 },
1202 canonical_value = $crate::sorted_tag!(( $($dependency),+ )),
1203 make_canonical = $crate::reorder_closure!(( $($dependency),+ )),
1204 make_uncanonical = $crate::unreorder_closure!(( $($dependency),+ )),
1205
1206 index_id_fn = {
1207 static INDEX_ID: std::sync::atomic::AtomicUsize =
1211 std::sync::atomic::AtomicUsize::new(usize::MAX);
1212
1213 let index_id = INDEX_ID.load(std::sync::atomic::Ordering::Relaxed);
1215 if index_id != usize::MAX {
1216 return index_id;
1217 }
1218
1219 let mut type_ids = [$( <$dependency as $crate::entity::property::Property<$entity>>::type_id() ),+];
1223 type_ids.sort_unstable();
1224 match $crate::entity::multi_property::type_ids_to_multi_property_index(&type_ids) {
1226 Some(index) => {
1227 INDEX_ID.store(index, std::sync::atomic::Ordering::Relaxed);
1229 index
1230 },
1231 None => {
1232 let index = <Self as $crate::entity::property::Property<$entity>>::id();
1234 INDEX_ID.store(index, std::sync::atomic::Ordering::Relaxed);
1235 $crate::entity::multi_property::register_type_ids_to_multi_property_index(
1237 &type_ids,
1238 index
1239 );
1240 index
1241 }
1242 }
1243 },
1244
1245 collect_deps_fn = | deps: &mut $crate::HashSet<usize> | {
1246 $(
1247 if <$dependency as $crate::entity::property::Property<$entity>>::is_derived() {
1248 <$dependency as $crate::entity::property::Property<$entity>>::collect_non_derived_dependencies(deps);
1249 } else {
1250 deps.insert(<$dependency as $crate::entity::property::Property<$entity>>::id());
1251 }
1252 )*
1253 },
1254
1255 display_impl = |val: &( $($dependency),+ )| {
1256 let ( $( [<_ $dependency:lower>] ),+ ) = val;
1257 let mut displayed = String::from("(");
1258 $(
1259 displayed.push_str(
1260 &<$dependency as $crate::entity::property::Property<$entity>>::get_display([<_ $dependency:lower>])
1261 );
1262 displayed.push_str(", ");
1263 )+
1264 displayed.truncate(displayed.len() - 2);
1265 displayed.push_str(")");
1266 displayed
1267 },
1268
1269 ctor_registration = {
1270 let _ = < [<$($dependency)*>] as $crate::entity::property::Property::<$entity> >::index_id();
1272 $crate::entity::property_store::add_to_property_registry::<$entity, [<$($dependency)*>]>();
1273 }
1274 );
1275
1276 }
1277 };
1278 }
1279
1280#[cfg(test)]
1281mod tests {
1282 #![allow(dead_code)]
1284
1285 use crate::entity::{PropertyIndexType, QueryInternal};
1286 use crate::prelude::*;
1287 use crate::with;
1288
1289 define_entity!(Person);
1290 define_entity!(Group);
1291
1292 define_property!(struct Pu32(u32), Person, default_const = Pu32(0));
1293 define_property!(struct POu32(Option<u32>), Person, default_const = POu32(None));
1294 define_property!(
1295 struct POFloat(Option<f64>),
1296 Person,
1297 impl_eq_hash = both,
1298 default_const = POFloat(None)
1299 );
1300 define_property!(
1301 struct POu32Custom(Option<u32>),
1302 Person,
1303 default_const = POu32Custom(None),
1304 display_impl = |value: &POu32Custom| match value.0 {
1305 Some(v) => format!("custom:{v}"),
1306 None => "custom:none".to_string(),
1307 }
1308 );
1309 define_property!(struct Name(&'static str), Person, default_const = Name(""));
1310 define_property!(struct Age(u8), Person, default_const = Age(0));
1311 define_property!(struct Weight(f64), Person, impl_eq_hash = both, default_const = Weight(0.0));
1312
1313 define_property!(
1315 struct Innocculation {
1316 time: f64,
1317 dose: u8,
1318 },
1319 Person,
1320 impl_eq_hash = both,
1321 default_const = Innocculation { time: 0.0, dose: 0 }
1322 );
1323
1324 define_property!(
1326 enum InfectionStatus {
1327 Susceptible,
1328 Infected,
1329 Recovered,
1330 },
1331 Person,
1332 default_const = InfectionStatus::Susceptible
1333 );
1334
1335 define_derived_property!(
1337 enum AgeGroup {
1338 Child,
1339 Adult,
1340 Senior,
1341 },
1342 Person,
1343 [Age], |age| {
1345 let age: Age = age;
1346 if age.0 < 18 {
1347 AgeGroup::Child
1348 } else if age.0 < 65 {
1349 AgeGroup::Adult
1350 } else {
1351 AgeGroup::Senior
1352 }
1353 }
1354 );
1355
1356 define_derived_property!(struct DerivedProp(bool), Person, [Age],
1358 |age| {
1359 DerivedProp(age.0 % 2 == 0)
1360 }
1361 );
1362
1363 define_derived_property!(
1364 struct DerivedMaybeAge(Option<u8>),
1365 Person,
1366 [Age],
1367 |age| DerivedMaybeAge((age.0 != 0).then_some(age.0))
1368 );
1369
1370 define_derived_property!(
1371 struct DerivedMaybeWeight(Option<f64>),
1372 Person,
1373 [Age],
1374 |age| DerivedMaybeWeight((age.0 != 0).then_some(age.0 as f64)),
1375 impl_eq_hash = both
1376 );
1377
1378 define_derived_property!(
1379 struct DerivedMaybeAgeCustom(Option<u8>),
1380 Person,
1381 [Age],
1382 |age| DerivedMaybeAgeCustom((age.0 != 0).then_some(age.0)),
1383 display_impl = |value: &DerivedMaybeAgeCustom| match value.0 {
1384 Some(v) => format!("derived:{v}"),
1385 None => "derived:none".to_string(),
1386 }
1387 );
1388
1389 define_derived_property!(
1390 struct DerivedWeight(f64),
1391 Person,
1392 [Age],
1393 |age| DerivedWeight(age.0 as f64),
1394 impl_eq_hash = both
1395 );
1396
1397 #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
1399 pub enum InfectionKind {
1400 Respiratory,
1401 Genetic,
1402 Superficial,
1403 }
1404 impl_property!(
1405 InfectionKind,
1406 Person,
1407 default_const = InfectionKind::Respiratory
1408 );
1409 impl_property!(InfectionKind, Group, default_const = InfectionKind::Genetic);
1410
1411 define_multi_property!((Name, Age, Weight), Person);
1412 define_multi_property!((Age, Weight, Name), Person);
1413 define_multi_property!((Weight, Age, Name), Person);
1414
1415 type ProfileNAW = (Name, Age, Weight);
1417 type ProfileAWN = (Age, Weight, Name);
1418 type ProfileWAN = (Weight, Age, Name);
1419
1420 #[test]
1421 fn test_multi_property_ordering() {
1422 let a = (Name("Jane"), Age(22), Weight(180.5));
1423 let b = (Age(22), Weight(180.5), Name("Jane"));
1424 let c = (Weight(180.5), Age(22), Name("Jane"));
1425
1426 assert_eq!(ProfileNAW::index_id(), ProfileAWN::index_id());
1428 assert_eq!(ProfileNAW::index_id(), ProfileWAN::index_id());
1429
1430 let a_canonical: <ProfileNAW as Property<_>>::CanonicalValue =
1431 ProfileNAW::make_canonical(a);
1432 let b_canonical: <ProfileAWN as Property<_>>::CanonicalValue =
1433 ProfileAWN::make_canonical(b);
1434 let c_canonical: <ProfileWAN as Property<_>>::CanonicalValue =
1435 ProfileWAN::make_canonical(c);
1436
1437 assert_eq!(a_canonical, b_canonical);
1438 assert_eq!(a_canonical, c_canonical);
1439
1440 assert_eq!(
1442 crate::hashing::one_shot_128(&a_canonical),
1443 crate::hashing::one_shot_128(&b_canonical)
1444 );
1445 assert_eq!(
1446 crate::hashing::one_shot_128(&a_canonical),
1447 crate::hashing::one_shot_128(&c_canonical)
1448 );
1449
1450 assert_eq!(ProfileNAW::make_uncanonical(b_canonical), a);
1453 assert_eq!(ProfileAWN::make_uncanonical(c_canonical), b);
1454 assert_eq!(ProfileWAN::make_uncanonical(a_canonical), c);
1455 }
1456
1457 #[test]
1458 fn test_multi_property_vs_property_query() {
1459 let mut context = Context::new();
1460
1461 context
1462 .add_entity(with!(Person, Name("John"), Age(42), Weight(220.5)))
1463 .unwrap();
1464 context
1465 .add_entity(with!(Person, Name("Jane"), Age(22), Weight(180.5)))
1466 .unwrap();
1467 context
1468 .add_entity(with!(Person, Name("Bob"), Age(32), Weight(190.5)))
1469 .unwrap();
1470 context
1471 .add_entity(with!(Person, Name("Alice"), Age(22), Weight(170.5)))
1472 .unwrap();
1473
1474 context.index_property::<_, ProfileNAW>();
1475
1476 assert!(context.is_property_indexed::<Person, ProfileNAW>());
1478 assert!(context.is_property_indexed::<Person, ProfileAWN>());
1479 assert!(context.is_property_indexed::<Person, ProfileWAN>());
1480 let mut indexed_count = 0;
1482 if context
1483 .get_property_value_store::<Person, ProfileNAW>()
1484 .index_type()
1485 != PropertyIndexType::Unindexed
1486 {
1487 indexed_count += 1;
1488 }
1489 if context
1490 .get_property_value_store::<Person, ProfileAWN>()
1491 .index_type()
1492 != PropertyIndexType::Unindexed
1493 {
1494 indexed_count += 1;
1495 }
1496 if context
1497 .get_property_value_store::<Person, ProfileWAN>()
1498 .index_type()
1499 != PropertyIndexType::Unindexed
1500 {
1501 indexed_count += 1;
1502 }
1503 assert_eq!(indexed_count, 1);
1504
1505 {
1506 let example_query = (Name("Alice"), Age(22), Weight(170.5));
1507 let query_multi_property_id =
1508 <(Name, Age, Weight) as QueryInternal<Person>>::multi_property_id(&example_query);
1509 assert!(query_multi_property_id.is_some());
1510 assert_eq!(ProfileNAW::index_id(), query_multi_property_id.unwrap());
1511 let query_parts = QueryInternal::query_parts(&example_query);
1512 assert_eq!(
1513 ProfileNAW::canonical_from_sorted_query_parts(query_parts.as_ref()),
1514 Some((Name("Alice"), Age(22), Weight(170.5)).make_canonical())
1515 );
1516 }
1517
1518 context.with_query_results(
1519 with!(Person, (Name("John"), Age(42), Weight(220.5))),
1520 &mut |results| {
1521 assert_eq!(results.into_iter().count(), 1);
1522 },
1523 );
1524 }
1525
1526 #[test]
1527 fn test_derived_property() {
1528 let mut context = Context::new();
1529
1530 let senior = context
1531 .add_entity::<Person, _>(with!(Person, Age(92)))
1532 .unwrap();
1533 let child = context
1534 .add_entity::<Person, _>(with!(Person, Age(12)))
1535 .unwrap();
1536 let adult = context
1537 .add_entity::<Person, _>(with!(Person, Age(44)))
1538 .unwrap();
1539
1540 let senior_group: AgeGroup = context.get_property(senior);
1541 let child_group: AgeGroup = context.get_property(child);
1542 let adult_group: AgeGroup = context.get_property(adult);
1543
1544 assert_eq!(senior_group, AgeGroup::Senior);
1545 assert_eq!(child_group, AgeGroup::Child);
1546 assert_eq!(adult_group, AgeGroup::Adult);
1547
1548 assert!(Age::non_derived_dependencies().is_empty());
1550 assert_eq!(AgeGroup::non_derived_dependencies(), [Age::id()]);
1552
1553 let mut expected_dependents = [
1555 AgeGroup::id(),
1556 DerivedProp::id(),
1557 DerivedMaybeAge::id(),
1558 DerivedMaybeWeight::id(),
1559 DerivedMaybeAgeCustom::id(),
1560 DerivedWeight::id(),
1561 ProfileNAW::id(),
1562 ProfileAWN::id(),
1563 ProfileWAN::id(),
1564 ];
1565 expected_dependents.sort_unstable();
1566 assert_eq!(Age::dependents(), expected_dependents);
1567 }
1568
1569 #[test]
1570 fn test_get_display() {
1571 let mut context = Context::new();
1572 let person = context
1573 .add_entity(with!(Person, POu32(Some(42)), Pu32(22)))
1574 .unwrap();
1575 assert_eq!(
1576 format!(
1577 "{:}",
1578 POu32::get_display(&context.get_property::<_, POu32>(person))
1579 ),
1580 "42"
1581 );
1582 assert_eq!(
1583 format!(
1584 "{:}",
1585 Pu32::get_display(&context.get_property::<_, Pu32>(person))
1586 ),
1587 "Pu32(22)"
1588 );
1589 let person2 = context
1590 .add_entity(with!(Person, POu32(None), Pu32(11)))
1591 .unwrap();
1592 assert_eq!(
1593 format!(
1594 "{:}",
1595 POu32::get_display(&context.get_property::<_, POu32>(person2))
1596 ),
1597 "None"
1598 );
1599 }
1600
1601 #[test]
1602 fn test_option_property_display_patterns() {
1603 let mut context = Context::new();
1604
1605 let some_person = context
1606 .add_entity(with!(
1607 Person,
1608 POu32(Some(42)),
1609 POFloat(Some(3.5)),
1610 POu32Custom(Some(7)),
1611 Pu32(1),
1612 ))
1613 .unwrap();
1614 let none_person = context
1615 .add_entity(with!(
1616 Person,
1617 POu32(None),
1618 POFloat(None),
1619 POu32Custom(None),
1620 Pu32(2)
1621 ))
1622 .unwrap();
1623
1624 assert_eq!(
1625 POu32::get_display(&context.get_property::<_, POu32>(some_person)),
1626 "42"
1627 );
1628 assert_eq!(
1629 POu32::get_display(&context.get_property::<_, POu32>(none_person)),
1630 "None"
1631 );
1632
1633 assert_eq!(
1634 POFloat::get_display(&context.get_property::<_, POFloat>(some_person)),
1635 "3.5"
1636 );
1637 assert_eq!(
1638 POFloat::get_display(&context.get_property::<_, POFloat>(none_person)),
1639 "None"
1640 );
1641
1642 assert_eq!(
1643 POu32Custom::get_display(&context.get_property::<_, POu32Custom>(some_person)),
1644 "custom:7"
1645 );
1646 assert_eq!(
1647 POu32Custom::get_display(&context.get_property::<_, POu32Custom>(none_person)),
1648 "custom:none"
1649 );
1650 }
1651
1652 #[test]
1653 fn test_option_derived_property_display_patterns() {
1654 let mut context = Context::new();
1655
1656 let some_person = context
1657 .add_entity::<Person, _>(with!(Person, Age(42)))
1658 .unwrap();
1659 let none_person = context
1660 .add_entity::<Person, _>(with!(Person, Age(0)))
1661 .unwrap();
1662
1663 assert_eq!(
1664 DerivedMaybeAge::get_display(&context.get_property::<_, DerivedMaybeAge>(some_person)),
1665 "42"
1666 );
1667 assert_eq!(
1668 DerivedMaybeAge::get_display(&context.get_property::<_, DerivedMaybeAge>(none_person)),
1669 "None"
1670 );
1671
1672 assert_eq!(
1673 DerivedMaybeWeight::get_display(
1674 &context.get_property::<_, DerivedMaybeWeight>(some_person)
1675 ),
1676 "42.0"
1677 );
1678 assert_eq!(
1679 DerivedMaybeWeight::get_display(
1680 &context.get_property::<_, DerivedMaybeWeight>(none_person)
1681 ),
1682 "None"
1683 );
1684
1685 assert_eq!(
1686 DerivedMaybeAgeCustom::get_display(
1687 &context.get_property::<_, DerivedMaybeAgeCustom>(some_person)
1688 ),
1689 "derived:42"
1690 );
1691 assert_eq!(
1692 DerivedMaybeAgeCustom::get_display(
1693 &context.get_property::<_, DerivedMaybeAgeCustom>(none_person)
1694 ),
1695 "derived:none"
1696 );
1697 }
1698
1699 #[test]
1700 fn test_debug_trait() {
1701 let property = Pu32(11);
1702 let debug_str = format!("{:?}", property);
1703 assert_eq!(debug_str, "Pu32(11)");
1704
1705 let property = POu32(Some(22));
1706 let debug_str = format!("{:?}", property);
1707 assert_eq!(debug_str, "POu32(Some(22))");
1708 }
1709
1710 #[test]
1711 fn test_define_derived_property_impl_eq_hash() {
1712 let mut values = crate::HashSet::default();
1713 values.insert(DerivedWeight(3.0));
1714 assert!(values.contains(&DerivedWeight(3.0)));
1715 }
1716}