1#[macro_export]
205macro_rules! define_property {
206 (
225 struct $name:ident ( $visibility:vis Option<$inner_ty:ty> ),
226 $entity:ident,
227 impl_eq_hash = $impl_eq_hash:ident
228 $(, $($extra:tt)*)?
229 ) => {
230 $crate::define_property!(
231 @apply_property_decoration $impl_eq_hash,
232 pub struct $name(pub Option<$inner_ty>);,
233 $name
234 );
235 $crate::impl_property!(@with_option_display_default $name, $entity $(, $($extra)*)?);
236 };
237 (
238 struct $name:ident ( $visibility:vis Option<$inner_ty:ty> ),
239 $entity:ident
240 $(, $($extra:tt)*)?
241 ) => {
242 $crate::define_property!(
243 @apply_property_decoration ,
244 pub struct $name(pub Option<$inner_ty>);,
245 $name
246 );
247 $crate::impl_property!(@with_option_display_default $name, $entity $(, $($extra)*)?);
248 };
249
250 (
251 struct $name:ident ( $($visibility:vis $field_ty:ty),* $(,)? ),
252 $entity:ident,
253 impl_eq_hash = $impl_eq_hash:ident
254 $(, $($extra:tt)*)?
255 ) => {
256 $crate::define_property!(
257 @apply_property_decoration $impl_eq_hash,
258 pub struct $name($(pub $field_ty),*);,
259 $name
260 );
261 $crate::impl_property!($name, $entity $(, $($extra)*)?);
262 };
263 (
264 struct $name:ident ( $($visibility:vis $field_ty:ty),* $(,)? ),
265 $entity:ident
266 $(, $($extra:tt)*)?
267 ) => {
268 $crate::define_property!(
269 @apply_property_decoration ,
270 pub struct $name($(pub $field_ty),*);,
271 $name
272 );
273 $crate::impl_property!($name, $entity $(, $($extra)*)?);
274 };
275
276 (
277 struct $name:ident { $($visibility:vis $field_name:ident : $field_ty:ty),* $(,)? },
278 $entity:ident,
279 impl_eq_hash = $impl_eq_hash:ident
280 $(, $($extra:tt)*)?
281 ) => {
282 $crate::define_property!(
283 @apply_property_decoration $impl_eq_hash,
284 pub struct $name { $(pub $field_name : $field_ty),* },
285 $name
286 );
287 $crate::impl_property!($name, $entity $(, $($extra)*)?);
288 };
289 (
290 struct $name:ident { $($visibility:vis $field_name:ident : $field_ty:ty),* $(,)? },
291 $entity:ident
292 $(, $($extra:tt)*)?
293 ) => {
294 $crate::define_property!(
295 @apply_property_decoration ,
296 pub struct $name { $(pub $field_name : $field_ty),* },
297 $name
298 );
299 $crate::impl_property!($name, $entity $(, $($extra)*)?);
300 };
301
302 (
303 enum $name:ident {
304 $($variant:ident),* $(,)?
305 },
306 $entity:ident,
307 impl_eq_hash = $impl_eq_hash:ident
308 $(, $($extra:tt)*)?
309 ) => {
310 $crate::define_property!(
311 @apply_property_decoration $impl_eq_hash,
312 pub enum $name { $($variant),* },
313 $name
314 );
315 $crate::impl_property!($name, $entity $(, $($extra)*)?);
316 };
317 (
318 enum $name:ident {
319 $($variant:ident),* $(,)?
320 },
321 $entity:ident
322 $(, $($extra:tt)*)?
323 ) => {
324 $crate::define_property!(
325 @apply_property_decoration ,
326 pub enum $name { $($variant),* },
327 $name
328 );
329 $crate::impl_property!($name, $entity $(, $($extra)*)?);
330 };
331
332 (@apply_property_decoration , $item:item, $name:ident) => {
335 #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, serde::Serialize, serde::Deserialize)]
336 $item
337 };
338 (@apply_property_decoration Eq, $item:item, $name:ident) => {
339 #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, $crate::rkyv::Archive, $crate::rkyv::Serialize)]
340 #[rkyv(crate = $crate::rkyv)]
341 $item
342 $crate::impl_property_eq!($name);
343 };
344 (@apply_property_decoration Hash, $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::impl_property_hash!($name);
349 };
350 (@apply_property_decoration both, $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::impl_property_eq_hash!($name);
355 };
356 (@apply_property_decoration neither, $item:item, $name:ident) => {
357 #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
358 $item
359 };
360 (@apply_property_decoration $mode:ident, $item:item, $name:ident) => {
361 compile_error!("`impl_eq_hash` must be one of `Eq`, `Hash`, `both`, or `neither`");
362 };
363
364}
365
366#[macro_export]
377macro_rules! impl_property_eq {
378 ($name:ident) => {
379 impl core::cmp::PartialEq for $name {
380 fn eq(&self, other: &Self) -> bool {
381 const N: usize = core::mem::size_of::<<$name as $crate::rkyv::Archive>::Archived>();
382
383 let left = $crate::rkyv::api::high::to_bytes_in::<_, $crate::rkyv::rancor::Error>(
384 self,
385 $crate::hashing::EqualityBufferWriter::<N>::new(),
386 )
387 .expect("serializing left value for equality comparison failed");
388
389 let right = $crate::rkyv::api::high::to_bytes_in::<_, $crate::rkyv::rancor::Error>(
390 other,
391 $crate::hashing::EqualityBufferWriter::<N>::new(),
392 )
393 .expect("serializing right value for equality comparison failed");
394
395 left.as_written() == right.as_written()
396 }
397 }
398
399 impl core::cmp::Eq for $name {}
400 };
401}
402
403#[macro_export]
414macro_rules! impl_property_hash {
415 ($name:ident) => {
416 impl core::hash::Hash for $name {
417 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
418 $crate::rkyv::api::high::to_bytes_in::<_, $crate::rkyv::rancor::Error>(
419 self,
420 $crate::hashing::HasherWriter::new(state),
421 )
422 .expect("serialization failed while hashing");
423 }
424 }
425 };
426}
427
428#[macro_export]
437macro_rules! impl_property_eq_hash {
438 ($name:ident) => {
439 $crate::impl_property_eq!($name);
440 $crate::impl_property_hash!($name);
441 };
442}
443
444#[macro_export]
499macro_rules! impl_property {
500 (
501 $property:ident,
502 $entity:ident
503 $(, compute_derived_fn = $compute_derived_fn:expr)?
504 $(, default_const = $default_const:expr)?
505 $(, collect_deps_fn = $collect_deps_fn:expr)?
506 $(, display_impl = $display_impl:expr)?
507 $(, ctor_registration = $ctor_registration:expr)?
508 ) => {
509 $crate::impl_property!(@assert_not_both $($compute_derived_fn)? ; $($default_const)?);
511
512 $crate::impl_property!(
513 @__impl_property_common
514 $property,
515 $entity,
516
517 $crate::impl_property!(@select_initialization_kind $($compute_derived_fn)? ; $($default_const)?),
519
520 $crate::impl_property!(
522 @unwrap_or
523 $($compute_derived_fn)?,
524 |_, _| panic!("property {} is not derived", stringify!($property))
525 ),
526
527 $crate::impl_property!(
529 @unwrap_or
530 $($default_const)?,
531 panic!("property {} has no default value", stringify!($property))
532 ),
533
534 [&'a dyn std::any::Any; 1],
536
537 {
539 |parts: &[&dyn std::any::Any]| -> Option<$property> {
540 let [part] = parts else {
541 return None;
542 };
543 part.downcast_ref::<$property>().copied()
544 }
545 },
546
547 {
549 fn default_query_parts_for_value<'a>(value: &'a $property) -> [&'a dyn std::any::Any; 1] {
550 [value as &'a dyn std::any::Any]
551 }
552
553 default_query_parts_for_value
554 },
555
556 {
558 std::any::TypeId::of::<Self>()
559 },
560
561 $crate::impl_property!(@unwrap_or $($display_impl)?, |v| format!("{v:?}")),
563
564 $crate::impl_property!(
566 @unwrap_or
567 $($collect_deps_fn)?,
568 |_| {}
569 ),
570
571 $crate::impl_property!(@unwrap_or $($ctor_registration)?, {
573 $crate::entity::property_store::add_to_property_registry::<$entity, $property>();
574 }),
575 );
576 };
577
578 (
579 @with_option_display_default
580 $property:ident,
581 $entity:ident
582 $(, compute_derived_fn = $compute_derived_fn:expr)?
583 $(, default_const = $default_const:expr)?
584 $(, collect_deps_fn = $collect_deps_fn:expr)?
585 $(, display_impl = $display_impl:expr)?
586 $(, ctor_registration = $ctor_registration:expr)?
587 ) => {
588 $crate::impl_property!(
589 $property,
590 $entity
591 $(, compute_derived_fn = $compute_derived_fn)?
592 $(, default_const = $default_const)?
593 $(, collect_deps_fn = $collect_deps_fn)?
594 , display_impl = $crate::impl_property!(@unwrap_or $($display_impl)?, |value: &Self| {
595 match value.0 {
596 Some(v) => format!("{:?}", v),
597 None => "None".to_string(),
598 }
599 })
600 $(, ctor_registration = $ctor_registration)?
601 );
602 };
603
604 (
605 @multi_property
606 $property:ident,
607 $entity:ident,
608 ( $($dependency:ident),+ )
609 $(, compute_derived_fn = $compute_derived_fn:expr)?
610 $(, default_const = $default_const:expr)?
611 $(, collect_deps_fn = $collect_deps_fn:expr)?
612 $(, display_impl = $display_impl:expr)?
613 $(, ctor_registration = $ctor_registration:expr)?
614 ) => {
615 $crate::impl_property!(@assert_not_both $($compute_derived_fn)? ; $($default_const)?);
616
617 $crate::impl_property!(
618 @__impl_property_common
619 $property,
620 $entity,
621 $crate::impl_property!(@select_initialization_kind $($compute_derived_fn)? ; $($default_const)?),
622 $crate::impl_property!(
623 @unwrap_or
624 $($compute_derived_fn)?,
625 |_, _| panic!("property {} is not derived", stringify!($property))
626 ),
627 $crate::impl_property!(
628 @unwrap_or
629 $($default_const)?,
630 panic!("property {} has no default value", stringify!($property))
631 ),
632 [&'a dyn std::any::Any; $crate::impl_property!(@count_tts $($dependency),+)],
633 {
634 $crate::paste::paste! {
635 #[allow(unused_assignments)]
636 |parts: &[&dyn std::any::Any]| -> Option<$property> {
637 let [$( [<p_ $dependency:lower>] ),+,] = parts else {
638 return None;
639 };
640 let sorted_parts = [$( [<p_ $dependency:lower>] ),+];
641 let keys = [
642 $(
643 <$dependency as $crate::entity::property::Property<$entity>>::type_id(),
644 )+
645 ];
646 let indices = $crate::entity::multi_property::static_sorted_indices(&keys);
647 let inverse = $crate::entity::multi_property::static_inverse_indices(&indices);
648 let mut declared_index = 0usize;
649 Some((
650 $(
651 {
652 let value = *sorted_parts[inverse[declared_index]].downcast_ref::<$dependency>()?;
653 declared_index += 1;
654 value
655 },
656 )+
657 ))
658 }
659 }
660 },
661 {
662 $crate::paste::paste! {
663 fn multi_property_query_parts_for_value<'a>(
664 value: &'a $property,
665 ) -> [&'a dyn std::any::Any; $crate::impl_property!(@count_tts $($dependency),+)] {
666 let keys = [
667 $(
668 <$dependency as $crate::entity::property::Property<$entity>>::type_id(),
669 )+
670 ];
671 let ( $( [<_ $dependency:lower>] ),+ ) = value;
672 let mut parts = [
673 $(
674 [<_ $dependency:lower>] as &'a dyn std::any::Any,
675 )+
676 ];
677 $crate::entity::multi_property::static_reorder_by_keys(&keys, &mut parts);
678 parts
679 }
680
681 multi_property_query_parts_for_value
682 }
683 },
684 {
685 std::any::TypeId::of::<Self>()
686 },
687 $crate::impl_property!(@unwrap_or $($display_impl)?, |v| format!("{v:?}")),
688 $crate::impl_property!(@unwrap_or $($collect_deps_fn)?, |_| {}),
689 $crate::impl_property!(@unwrap_or $($ctor_registration)?, {
690 $crate::entity::property_store::add_to_property_registry::<$entity, $property>();
691 }),
692 );
693 };
694
695 (@assert_not_both $compute_derived_fn:expr ; $default_const:expr) => {
697 compile_error!(
698 "impl_property!: `compute_derived_fn = ...` (derived property) and `default_const = ...` \
699 (non-derived property default constant) are mutually exclusive. Remove one of them."
700 );
701 };
702 (@assert_not_both $compute_derived_fn:expr ; ) => {};
703 (@assert_not_both ; $default_const:expr) => {};
704 (@assert_not_both ; ) => {};
705
706 (@select_initialization_kind $compute_derived_fn:expr ; $default_const:expr) => {
708 compile_error!(
711 "impl_property!: cannot select initialization kind because both `compute_derived_fn` \
712 and `default_const` are present"
713 )
714 };
715 (@select_initialization_kind $compute_derived_fn:expr ; ) => {
716 $crate::entity::property::PropertyInitializationKind::Derived
717 };
718 (@select_initialization_kind ; $default_const:expr) => {
719 $crate::entity::property::PropertyInitializationKind::Constant
720 };
721 (@select_initialization_kind ; ) => {
722 $crate::entity::property::PropertyInitializationKind::Explicit
723 };
724
725 (@unwrap_or $value:expr, $_default:expr) => { $value };
727 (@unwrap_or, $default:expr) => { $default };
728
729 (@replace_with_unit $_tt:tt) => { () };
730 (@count_tts $($tt:tt),* $(,)?) => {
731 <[()]>::len(&[$($crate::impl_property!(@replace_with_unit $tt)),*])
732 };
733
734 (
736 @__impl_property_common
737 $property:ident, $entity:ident, $initialization_kind:expr, $compute_derived_fn:expr, $default_const:expr, $query_parts_type:ty,
743 $value_from_query_parts_fn:expr,
744 $query_parts_for_value_fn:expr,
745 $type_id_fn:expr, $display_impl:expr, $collect_deps_fn:expr, $ctor_registration:expr, ) => {
750 impl $crate::entity::property::Property<$entity> for $property {
751 type QueryParts<'a> = $query_parts_type where Self: 'a;
752
753 const NAME: &'static str = stringify!($property);
754
755 fn initialization_kind() -> $crate::entity::property::PropertyInitializationKind {
756 $initialization_kind
757 }
758
759 fn compute_derived(
760 _context: &$crate::Context,
761 _entity_id: $crate::entity::EntityId<$entity>,
762 ) -> Self {
763 ($compute_derived_fn)(_context, _entity_id)
764 }
765
766 fn default_const() -> Self {
767 $default_const
768 }
769
770 fn value_from_query_parts(
771 parts: &[&dyn std::any::Any],
772 ) -> Option<Self> {
773 ($value_from_query_parts_fn)(parts)
774 }
775
776 fn query_parts_for_value(value: &Self) -> Self::QueryParts<'_> {
777 ($query_parts_for_value_fn)(value)
778 }
779
780 fn type_id() -> std::any::TypeId {
781 $type_id_fn
782 }
783
784 fn get_display(&self) -> String {
785 ($display_impl)(self)
786 }
787
788 fn id() -> usize {
789 static INDEX: std::sync::atomic::AtomicUsize =
793 std::sync::atomic::AtomicUsize::new(usize::MAX);
794
795 let index = INDEX.load(std::sync::atomic::Ordering::Relaxed);
797 if index != usize::MAX {
798 return index;
799 }
800
801 $crate::entity::property_store::initialize_property_id::<$entity>(&INDEX)
803 }
804
805 fn collect_non_derived_dependencies(result: &mut $crate::HashSet<usize>) {
806 ($collect_deps_fn)(result)
807 }
808 }
809
810 $crate::paste::paste! {
811 $crate::ctor::declarative::ctor!{
812 #[ctor(unsafe)]
813 fn [<_register_property_ $entity:snake _ $property:snake>]() {
814 $ctor_registration
815 }
816 }
817 }
818 };
819}
820
821#[macro_export]
835macro_rules! define_derived_property {
836 (
845 struct $name:ident ( $visibility:vis Option<$inner_ty:ty> ),
846 $entity:ident,
847 [$($dependency:ident),*]
848 $(, [$($global_dependency:ident),*])?,
849 |$($param:ident),+| $derive_fn:expr,
850 impl_eq_hash = $impl_eq_hash:ident
851 $(, $($extra:tt)+)*
852 ) => {
853 $crate::define_property!(
854 @apply_property_decoration
855 $impl_eq_hash,
856 pub struct $name(pub Option<$inner_ty>);,
857 $name
858 );
859
860 $crate::impl_derived_property!(
861 @with_option_display_default
862 $name,
863 $entity,
864 [$($dependency),*],
865 [$($($global_dependency),*)?],
866 |$($param),+| $derive_fn
867 $(, $($extra)+)*
868 );
869 };
870 (
871 struct $name:ident ( $visibility:vis Option<$inner_ty:ty> ),
872 $entity:ident,
873 [$($dependency:ident),*]
874 $(, [$($global_dependency:ident),*])?,
875 |$($param:ident),+| $derive_fn:expr
876 $(, $($extra:tt)+)*
877 ) => {
878 $crate::define_property!(
879 @apply_property_decoration
880 ,
881 pub struct $name(pub Option<$inner_ty>);,
882 $name
883 );
884
885 $crate::impl_derived_property!(
886 @with_option_display_default
887 $name,
888 $entity,
889 [$($dependency),*],
890 [$($($global_dependency),*)?],
891 |$($param),+| $derive_fn
892 $(, $($extra)+)*
893 );
894 };
895
896 (
898 struct $name:ident ( $($visibility:vis $field_ty:ty),* $(,)? ),
899 $entity:ident,
900 [$($dependency:ident),*]
901 $(, [$($global_dependency:ident),*])?,
902 |$($param:ident),+| $derive_fn:expr,
903 impl_eq_hash = $impl_eq_hash:ident
904 $(, $($extra:tt)+)*
905 ) => {
906 $crate::define_property!(
907 @apply_property_decoration
908 $impl_eq_hash,
909 pub struct $name( $(pub $field_ty),* );,
910 $name
911 );
912
913 $crate::impl_derived_property!(
914 $name,
915 $entity,
916 [$($dependency),*],
917 [$($($global_dependency),*)?],
918 |$($param),+| $derive_fn
919 $(, $($extra)+)*
920 );
921 };
922 (
923 struct $name:ident ( $($visibility:vis $field_ty:ty),* $(,)? ),
924 $entity:ident,
925 [$($dependency:ident),*]
926 $(, [$($global_dependency:ident),*])?,
927 |$($param:ident),+| $derive_fn:expr
928 $(, $($extra:tt)+)*
929 ) => {
930 $crate::define_property!(
931 @apply_property_decoration
932 ,
933 pub struct $name( $(pub $field_ty),* );,
934 $name
935 );
936
937 $crate::impl_derived_property!(
938 $name,
939 $entity,
940 [$($dependency),*],
941 [$($($global_dependency),*)?],
942 |$($param),+| $derive_fn
943 $(, $($extra)+)*
944 );
945 };
946
947 (
949 struct $name:ident { $($visibility:vis $field_name:ident : $field_ty:ty),* $(,)? },
950 $entity:ident,
951 [$($dependency:ident),*]
952 $(, [$($global_dependency:ident),*])?,
953 |$($param:ident),+| $derive_fn:expr,
954 impl_eq_hash = $impl_eq_hash:ident
955 $(, $($extra:tt)+)*
956 ) => {
957 $crate::define_property!(
958 @apply_property_decoration
959 $impl_eq_hash,
960 pub struct $name { $($visibility $field_name : $field_ty),* },
961 $name
962 );
963
964 $crate::impl_derived_property!(
965 $name,
966 $entity,
967 [$($dependency),*],
968 [$($($global_dependency),*)?],
969 |$($param),+| $derive_fn
970 $(, $($extra)+)*
971 );
972 };
973 (
974 struct $name:ident { $($visibility:vis $field_name:ident : $field_ty:ty),* $(,)? },
975 $entity:ident,
976 [$($dependency:ident),*]
977 $(, [$($global_dependency:ident),*])?,
978 |$($param:ident),+| $derive_fn:expr
979 $(, $($extra:tt)+)*
980 ) => {
981 $crate::define_property!(
982 @apply_property_decoration
983 ,
984 pub struct $name { $($visibility $field_name : $field_ty),* },
985 $name
986 );
987
988 $crate::impl_derived_property!(
989 $name,
990 $entity,
991 [$($dependency),*],
992 [$($($global_dependency),*)?],
993 |$($param),+| $derive_fn
994 $(, $($extra)+)*
995 );
996 };
997
998 (
1000 enum $name:ident {
1001 $($variant:ident),* $(,)?
1002 },
1003 $entity:ident,
1004 [$($dependency:ident),*]
1005 $(, [$($global_dependency:ident),*])?,
1006 |$($param:ident),+| $derive_fn:expr,
1007 impl_eq_hash = $impl_eq_hash:ident
1008 $(, $($extra:tt)+)*
1009 ) => {
1010 $crate::define_property!(
1011 @apply_property_decoration
1012 $impl_eq_hash,
1013 pub enum $name {
1014 $($variant),*
1015 },
1016 $name
1017 );
1018
1019 $crate::impl_derived_property!(
1020 $name,
1021 $entity,
1022 [$($dependency),*],
1023 [$($($global_dependency),*)?],
1024 |$($param),+| $derive_fn
1025 $(, $($extra)+)*
1026 );
1027 };
1028 (
1029 enum $name:ident {
1030 $($variant:ident),* $(,)?
1031 },
1032 $entity:ident,
1033 [$($dependency:ident),*]
1034 $(, [$($global_dependency:ident),*])?,
1035 |$($param:ident),+| $derive_fn:expr
1036 $(, $($extra:tt)+)*
1037 ) => {
1038 $crate::define_property!(
1039 @apply_property_decoration
1040 ,
1041 pub enum $name {
1042 $($variant),*
1043 },
1044 $name
1045 );
1046
1047 $crate::impl_derived_property!(
1048 $name,
1049 $entity,
1050 [$($dependency),*],
1051 [$($($global_dependency),*)?],
1052 |$($param),+| $derive_fn
1053 $(, $($extra)+)*
1054 );
1055 };
1056}
1057
1058#[macro_export]
1064macro_rules! impl_derived_property {
1065 (
1066 $name:ident,
1067 $entity:ident,
1068 [$($dependency:ident),*]
1069 $(, [$($global_dependency:ident),*])?,
1070 |$($param:ident),+| $derive_fn:expr
1071 $(, $($extra:tt)+)*
1072 ) => {
1073 $crate::impl_property!(
1074 $name,
1075 $entity,
1076 compute_derived_fn = $crate::impl_derived_property!(
1077 @construct_compute_fn
1078 $entity,
1079 [$($dependency),*],
1080 [$($($global_dependency),*)?],
1081 |$($param),+| $derive_fn
1082 ),
1083 collect_deps_fn = | deps: &mut $crate::HashSet<usize> | {
1084 $(
1085 if <$dependency as $crate::entity::property::Property<$entity>>::is_derived() {
1086 <$dependency as $crate::entity::property::Property<$entity>>::collect_non_derived_dependencies(deps);
1087 } else {
1088 deps.insert(<$dependency as $crate::entity::property::Property<$entity>>::id());
1089 }
1090 )*
1091 }
1092 $(, $($extra)+)*
1093 );
1094 };
1095
1096 (
1098 @construct_compute_fn
1099 $entity:ident,
1100 [$($dependency:ident),*],
1101 [$($global_dependency:ident),*],
1102 |$($param:ident),+| $derive_fn:expr
1103 ) => {
1104 |context: &$crate::Context, entity_id| {
1105 #[allow(unused_imports)]
1106 use $crate::global_properties::ContextGlobalPropertiesExt;
1107 #[allow(unused_parens)]
1108 let ($($param,)*) = (
1109 $(context.get_property::<$entity, $dependency>(entity_id)),*,
1110 $(
1111 context.get_global_property_value($global_dependency)
1112 .expect(&format!("Global property {} not initialized", stringify!($global_dependency)))
1113 ),*
1114 );
1115 $derive_fn
1116 }
1117 };
1118
1119 (@unwrap_or $value:expr, $_default:expr) => { $value };
1120 (@unwrap_or, $default:expr) => { $default };
1121
1122 (
1123 @with_option_display_default
1124 $name:ident,
1125 $entity:ident,
1126 [$($dependency:ident),*],
1127 [$($global_dependency:ident),*],
1128 |$($param:ident),+| $derive_fn:expr
1129 $(, default_const = $default_const:expr)?
1130 $(, display_impl = $display_impl:expr)?
1131 $(, collect_deps_fn = $collect_deps_fn:expr)?
1132 $(, ctor_registration = $ctor_registration:expr)?
1133 ) => {
1134 $crate::impl_derived_property!(
1135 $name,
1136 $entity,
1137 [$($dependency),*],
1138 [$($global_dependency),*],
1139 |$($param),+| $derive_fn
1140 $(, default_const = $default_const)?
1141 , display_impl = $crate::impl_derived_property!(@unwrap_or $($display_impl)?, |value: &$name| {
1142 match value.0 {
1143 Some(v) => format!("{:?}", v),
1144 None => "None".to_string(),
1145 }
1146 })
1147 $(, collect_deps_fn = $collect_deps_fn)?
1148 $(, ctor_registration = $ctor_registration)?
1149 );
1150 };
1151
1152}
1153
1154#[macro_export]
1212macro_rules! define_multi_property {
1213 (@impl
1214 $entity:ident,
1215 $($dependency:ident),+
1216 ) => {
1217 $crate::paste::paste! {
1218 type [<$($dependency)*>] = ( $($dependency),+ );
1219
1220 $(
1222 const _: () = assert!(
1223 $crate::entity::property::const_str_eq(
1224 stringify!($dependency),
1225 <$dependency as $crate::entity::property::Property<$entity>>::NAME,
1226 ),
1227 concat!(
1228 "define_multi_property!: `",
1229 stringify!($dependency),
1230 "` is a type alias; use the underlying property type (see issue #843)."
1231 ),
1232 );
1233 )+
1234
1235 $crate::impl_property!(
1236 @multi_property
1237 [<$($dependency)*>],
1238 $entity,
1239 ( $($dependency),+ ),
1240 compute_derived_fn = |context: &$crate::Context, entity_id: $crate::entity::EntityId<$entity>| {
1241 (
1242 $(<$crate::Context as $crate::entity::ContextEntitiesExt>::get_property::<$entity, $dependency>(
1243 context,
1244 entity_id,
1245 )),+
1246 )
1247 },
1248
1249 collect_deps_fn = | deps: &mut $crate::HashSet<usize> | {
1250 $(
1251 if <$dependency as $crate::entity::property::Property<$entity>>::is_derived() {
1252 <$dependency as $crate::entity::property::Property<$entity>>::collect_non_derived_dependencies(deps);
1253 } else {
1254 deps.insert(<$dependency as $crate::entity::property::Property<$entity>>::id());
1255 }
1256 )*
1257 },
1258
1259 display_impl = |val: &( $($dependency),+ )| {
1260 let ( $( [<_ $dependency:lower>] ),+ ) = val;
1261 let mut displayed = String::from("(");
1262 $(
1263 displayed.push_str(
1264 &<$dependency as $crate::entity::property::Property<$entity>>::get_display([<_ $dependency:lower>])
1265 );
1266 displayed.push_str(", ");
1267 )+
1268 displayed.truncate(displayed.len() - 2);
1269 displayed.push_str(")");
1270 displayed
1271 },
1272
1273 ctor_registration = {
1274 let mut type_ids = [$( <$dependency as $crate::entity::property::Property<$entity>>::type_id() ),+];
1275 type_ids.sort_unstable();
1276 if let Some((_, existing_name)) =
1277 $crate::entity::multi_property::register_type_ids_to_multi_property_id(
1278 <$entity as $crate::entity::Entity>::id(),
1279 &type_ids,
1280 <[<$($dependency)*>] as $crate::entity::property::Property<$entity>>::type_id(),
1281 <[<$($dependency)*>] as $crate::entity::property::Property<$entity>>::id(),
1282 <[<$($dependency)*>] as $crate::entity::property::Property<$entity>>::name(),
1283 )
1284 {
1285 $crate::entity::multi_property::record_pre_main_warning(format!(
1286 "multi-property {} is equivalent to already registered multi-property {existing_name}; queries will resolve to {existing_name}, and attempting to index {} will panic",
1287 <[<$($dependency)*>] as $crate::entity::property::Property<$entity>>::name(),
1288 <[<$($dependency)*>] as $crate::entity::property::Property<$entity>>::name(),
1289 ));
1290 }
1291 $crate::entity::property_store::add_to_property_registry::<$entity, [<$($dependency)*>]>();
1292 }
1293 );
1294
1295 }
1296 };
1297
1298 (
1299 $entity:ident,
1300 ( $first:ident, $second:ident $(, $dependency:ident)* $(,)? )
1301 $(,)?
1302 ) => {
1303 $crate::define_multi_property!(@impl $entity, $first, $second $(, $dependency)*);
1304 };
1305
1306 () => {
1307 compile_error!(
1308 "define_multi_property!: expected `define_multi_property!(Entity, (PropertyA, PropertyB, ...))`"
1309 );
1310 };
1311
1312 ($entity:ident $(,)?) => {
1313 compile_error!(
1314 "define_multi_property!: multi-properties require at least two component properties"
1315 );
1316 };
1317
1318 (( $($dependency:ident),+ $(,)? ), $entity:ident $(,)?) => {
1319 compile_error!(
1320 "define_multi_property!: tuple-first syntax is no longer supported; write `define_multi_property!(Entity, (PropertyA, PropertyB, ...))`"
1321 );
1322 };
1323
1324 ($entity:ident, ( $only:ident $(,)? ) $(,)?) => {
1325 compile_error!(
1326 "define_multi_property!: multi-properties require at least two component properties"
1327 );
1328 };
1329
1330 (
1331 $entity:ident,
1332 $first:ident,
1333 $second:ident
1334 $(, $dependency:ident)*
1335 $(,)?
1336 ) => {
1337 compile_error!(
1338 "define_multi_property!: flat component syntax is no longer supported; write `define_multi_property!(Entity, (PropertyA, PropertyB, ...))`"
1339 );
1340 };
1341
1342 ($entity:ident, $only:ident $(,)?) => {
1343 compile_error!(
1344 "define_multi_property!: multi-properties require at least two component properties"
1345 );
1346 };
1347
1348 ($($tokens:tt)*) => {
1349 compile_error!(
1350 "define_multi_property!: expected `define_multi_property!(Entity, (PropertyA, PropertyB, ...))`"
1351 );
1352 };
1353 }
1354#[cfg(test)]
1355mod tests {
1356 #![allow(dead_code)]
1358
1359 use crate::entity::QueryInternal;
1360 use crate::prelude::*;
1361 use crate::with;
1362
1363 define_entity!(Person);
1364 define_entity!(Group);
1365
1366 define_property!(struct Pu32(u32), Person, default_const = Pu32(0));
1367 define_property!(struct POu32(Option<u32>), Person, default_const = POu32(None));
1368 define_property!(
1369 struct POFloat(Option<f64>),
1370 Person,
1371 impl_eq_hash = both,
1372 default_const = POFloat(None)
1373 );
1374 define_property!(
1375 struct POu32Custom(Option<u32>),
1376 Person,
1377 default_const = POu32Custom(None),
1378 display_impl = |value: &POu32Custom| match value.0 {
1379 Some(v) => format!("custom:{v}"),
1380 None => "custom:none".to_string(),
1381 }
1382 );
1383 define_property!(struct Name(&'static str), Person, default_const = Name(""));
1384 define_property!(struct Age(u8), Person, default_const = Age(0));
1385 define_property!(struct Weight(f64), Person, impl_eq_hash = both, default_const = Weight(0.0));
1386
1387 define_property!(
1389 struct Innocculation {
1390 time: f64,
1391 dose: u8,
1392 },
1393 Person,
1394 impl_eq_hash = both,
1395 default_const = Innocculation { time: 0.0, dose: 0 }
1396 );
1397
1398 define_property!(
1400 enum InfectionStatus {
1401 Susceptible,
1402 Infected,
1403 Recovered,
1404 },
1405 Person,
1406 default_const = InfectionStatus::Susceptible
1407 );
1408
1409 define_derived_property!(
1411 enum AgeGroup {
1412 Child,
1413 Adult,
1414 Senior,
1415 },
1416 Person,
1417 [Age], |age| {
1419 let age: Age = age;
1420 if age.0 < 18 {
1421 AgeGroup::Child
1422 } else if age.0 < 65 {
1423 AgeGroup::Adult
1424 } else {
1425 AgeGroup::Senior
1426 }
1427 }
1428 );
1429
1430 define_derived_property!(struct DerivedProp(bool), Person, [Age],
1432 |age| {
1433 DerivedProp(age.0 % 2 == 0)
1434 }
1435 );
1436
1437 define_derived_property!(
1438 struct DerivedMaybeAge(Option<u8>),
1439 Person,
1440 [Age],
1441 |age| DerivedMaybeAge((age.0 != 0).then_some(age.0))
1442 );
1443
1444 define_derived_property!(
1445 struct DerivedMaybeWeight(Option<f64>),
1446 Person,
1447 [Age],
1448 |age| DerivedMaybeWeight((age.0 != 0).then_some(age.0 as f64)),
1449 impl_eq_hash = both
1450 );
1451
1452 define_derived_property!(
1453 struct DerivedMaybeAgeCustom(Option<u8>),
1454 Person,
1455 [Age],
1456 |age| DerivedMaybeAgeCustom((age.0 != 0).then_some(age.0)),
1457 display_impl = |value: &DerivedMaybeAgeCustom| match value.0 {
1458 Some(v) => format!("derived:{v}"),
1459 None => "derived:none".to_string(),
1460 }
1461 );
1462
1463 define_derived_property!(
1464 struct DerivedWeight(f64),
1465 Person,
1466 [Age],
1467 |age| DerivedWeight(age.0 as f64),
1468 impl_eq_hash = both
1469 );
1470
1471 #[derive(Debug, PartialEq, Clone, Copy)]
1472 struct NonIndexableFloat(f64);
1473 impl_property!(
1474 NonIndexableFloat,
1475 Person,
1476 default_const = NonIndexableFloat(0.0)
1477 );
1478
1479 #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
1481 pub enum InfectionKind {
1482 Respiratory,
1483 Genetic,
1484 Superficial,
1485 }
1486 impl_property!(
1487 InfectionKind,
1488 Person,
1489 default_const = InfectionKind::Respiratory
1490 );
1491 impl_property!(InfectionKind, Group, default_const = InfectionKind::Genetic);
1492
1493 define_multi_property!(Person, (Name, Age, Weight));
1494 define_multi_property!(Person, (Age, Weight, Name));
1495 define_multi_property!(Person, (Weight, Age, Name));
1496 define_multi_property!(Person, (Name, Weight,),);
1497
1498 type ProfileNAW = (Name, Age, Weight);
1500 type ProfileAWN = (Age, Weight, Name);
1501 type ProfileWAN = (Weight, Age, Name);
1502 type ProfileNW = (Name, Weight);
1503
1504 define_entity!(SingleProfilePerson);
1505 define_property!(
1506 struct SingleName(&'static str),
1507 SingleProfilePerson,
1508 default_const = SingleName("")
1509 );
1510 define_property!(
1511 struct SingleAge(u8),
1512 SingleProfilePerson,
1513 default_const = SingleAge(0)
1514 );
1515 define_property!(
1516 struct SingleWeight(u8),
1517 SingleProfilePerson,
1518 default_const = SingleWeight(0)
1519 );
1520 define_multi_property!(SingleProfilePerson, (SingleName, SingleAge, SingleWeight));
1521 type SingleProfile = (SingleName, SingleAge, SingleWeight);
1522
1523 #[test]
1524 fn test_multi_property_ordering() {
1525 let a = (Name("Jane"), Age(22), Weight(180.5));
1526 let b = (Age(22), Weight(180.5), Name("Jane"));
1527 let c = (Weight(180.5), Age(22), Name("Jane"));
1528
1529 assert_ne!(ProfileNAW::id(), ProfileAWN::id());
1532 assert_ne!(ProfileNAW::id(), ProfileWAN::id());
1533 assert_ne!(ProfileNAW::type_id(), ProfileAWN::type_id());
1534 assert_ne!(ProfileNAW::type_id(), ProfileWAN::type_id());
1535 let _ = ProfileNW::id();
1536
1537 let query_parts = ProfileNAW::query_parts_for_value(&a);
1538 assert_eq!(
1539 ProfileAWN::value_from_query_parts(query_parts.as_ref()),
1540 Some(b)
1541 );
1542 assert_eq!(
1543 ProfileWAN::value_from_query_parts(query_parts.as_ref()),
1544 Some(c)
1545 );
1546 }
1547
1548 #[test]
1549 fn test_non_indexable_property_unindexed_behavior() {
1550 let mut context = Context::new();
1551
1552 let first = context
1553 .add_entity(with!(Person, NonIndexableFloat(1.5)))
1554 .unwrap();
1555 let _second = context
1556 .add_entity(with!(Person, NonIndexableFloat(2.5)))
1557 .unwrap();
1558
1559 assert_eq!(
1560 context.get_property::<Person, NonIndexableFloat>(first),
1561 NonIndexableFloat(1.5)
1562 );
1563
1564 context.set_property(first, NonIndexableFloat(3.5));
1565 assert_eq!(
1566 context.get_property::<Person, NonIndexableFloat>(first),
1567 NonIndexableFloat(3.5)
1568 );
1569
1570 let mut results = Vec::new();
1571 context.with_query_results(with!(Person, NonIndexableFloat(3.5)), &mut |entity_ids| {
1572 results = entity_ids.into_iter().collect::<Vec<_>>();
1573 });
1574 assert_eq!(results, vec![first]);
1575 assert_eq!(
1576 context.query_entity_count(with!(Person, NonIndexableFloat(3.5))),
1577 1
1578 );
1579 }
1580
1581 #[test]
1582 fn test_single_multi_property_vs_property_query() {
1583 let mut context = Context::new();
1584
1585 context
1586 .add_entity(with!(
1587 SingleProfilePerson,
1588 SingleName("John"),
1589 SingleAge(42),
1590 SingleWeight(220)
1591 ))
1592 .unwrap();
1593 context
1594 .add_entity(with!(
1595 SingleProfilePerson,
1596 SingleName("Jane"),
1597 SingleAge(22),
1598 SingleWeight(180)
1599 ))
1600 .unwrap();
1601 context
1602 .add_entity(with!(
1603 SingleProfilePerson,
1604 SingleName("Bob"),
1605 SingleAge(32),
1606 SingleWeight(190)
1607 ))
1608 .unwrap();
1609 context
1610 .add_entity(with!(
1611 SingleProfilePerson,
1612 SingleName("Alice"),
1613 SingleAge(22),
1614 SingleWeight(170)
1615 ))
1616 .unwrap();
1617
1618 context.index_property::<SingleProfilePerson, SingleProfile>();
1619
1620 let example_query = (SingleName("Alice"), SingleAge(22), SingleWeight(170));
1621 assert_eq!(
1622 <SingleProfile as QueryInternal<SingleProfilePerson>>::multi_property_id(
1623 &example_query
1624 ),
1625 Some(SingleProfile::id())
1626 );
1627 let query_parts = QueryInternal::query_parts(&example_query);
1628 assert_eq!(
1629 SingleProfile::value_from_query_parts(query_parts.as_ref()),
1630 Some((SingleName("Alice"), SingleAge(22), SingleWeight(170)))
1631 );
1632
1633 context.with_query_results(
1634 with!(
1635 SingleProfilePerson,
1636 (SingleName("John"), SingleAge(42), SingleWeight(220))
1637 ),
1638 &mut |results| {
1639 assert_eq!(results.into_iter().count(), 1);
1640 },
1641 );
1642 }
1643
1644 #[test]
1645 fn test_equivalent_multi_property_query_routing() {
1646 let example_query = (Name("Alice"), Age(22), Weight(170.5));
1647 let query_multi_property_id =
1648 <(Name, Age, Weight) as QueryInternal<Person>>::multi_property_id(&example_query);
1649
1650 assert!(matches!(
1651 query_multi_property_id,
1652 Some(id) if id == ProfileNAW::id() || id == ProfileAWN::id() || id == ProfileWAN::id()
1653 ));
1654
1655 let query_parts = QueryInternal::query_parts(&example_query);
1656 assert_eq!(
1657 ProfileNAW::value_from_query_parts(query_parts.as_ref()),
1658 Some((Name("Alice"), Age(22), Weight(170.5)))
1659 );
1660 assert_eq!(
1661 ProfileAWN::value_from_query_parts(query_parts.as_ref()),
1662 Some((Age(22), Weight(170.5), Name("Alice")))
1663 );
1664 assert_eq!(
1665 ProfileWAN::value_from_query_parts(query_parts.as_ref()),
1666 Some((Weight(170.5), Age(22), Name("Alice")))
1667 );
1668 }
1669
1670 #[test]
1671 fn test_derived_property() {
1672 let mut context = Context::new();
1673
1674 let senior = context
1675 .add_entity::<Person, _>(with!(Person, Age(92)))
1676 .unwrap();
1677 let child = context
1678 .add_entity::<Person, _>(with!(Person, Age(12)))
1679 .unwrap();
1680 let adult = context
1681 .add_entity::<Person, _>(with!(Person, Age(44)))
1682 .unwrap();
1683
1684 let senior_group: AgeGroup = context.get_property(senior);
1685 let child_group: AgeGroup = context.get_property(child);
1686 let adult_group: AgeGroup = context.get_property(adult);
1687
1688 assert_eq!(senior_group, AgeGroup::Senior);
1689 assert_eq!(child_group, AgeGroup::Child);
1690 assert_eq!(adult_group, AgeGroup::Adult);
1691
1692 assert!(Age::non_derived_dependencies().is_empty());
1694 assert_eq!(AgeGroup::non_derived_dependencies(), [Age::id()]);
1696
1697 let mut expected_dependents = [
1699 AgeGroup::id(),
1700 DerivedProp::id(),
1701 DerivedMaybeAge::id(),
1702 DerivedMaybeWeight::id(),
1703 DerivedMaybeAgeCustom::id(),
1704 DerivedWeight::id(),
1705 ProfileNAW::id(),
1706 ProfileAWN::id(),
1707 ProfileWAN::id(),
1708 ];
1709 expected_dependents.sort_unstable();
1710 assert_eq!(Age::dependents(), expected_dependents);
1711 }
1712
1713 #[test]
1714 fn test_get_display() {
1715 let mut context = Context::new();
1716 let person = context
1717 .add_entity(with!(Person, POu32(Some(42)), Pu32(22)))
1718 .unwrap();
1719 assert_eq!(
1720 POu32::get_display(&context.get_property::<_, POu32>(person)).to_string(),
1721 "42"
1722 );
1723 assert_eq!(
1724 Pu32::get_display(&context.get_property::<_, Pu32>(person)).to_string(),
1725 "Pu32(22)"
1726 );
1727 let person2 = context
1728 .add_entity(with!(Person, POu32(None), Pu32(11)))
1729 .unwrap();
1730 assert_eq!(
1731 POu32::get_display(&context.get_property::<_, POu32>(person2)).to_string(),
1732 "None"
1733 );
1734 }
1735
1736 #[test]
1737 fn test_option_property_display_patterns() {
1738 let mut context = Context::new();
1739
1740 let some_person = context
1741 .add_entity(with!(
1742 Person,
1743 POu32(Some(42)),
1744 POFloat(Some(3.5)),
1745 POu32Custom(Some(7)),
1746 Pu32(1),
1747 ))
1748 .unwrap();
1749 let none_person = context
1750 .add_entity(with!(
1751 Person,
1752 POu32(None),
1753 POFloat(None),
1754 POu32Custom(None),
1755 Pu32(2)
1756 ))
1757 .unwrap();
1758
1759 assert_eq!(
1760 POu32::get_display(&context.get_property::<_, POu32>(some_person)),
1761 "42"
1762 );
1763 assert_eq!(
1764 POu32::get_display(&context.get_property::<_, POu32>(none_person)),
1765 "None"
1766 );
1767
1768 assert_eq!(
1769 POFloat::get_display(&context.get_property::<_, POFloat>(some_person)),
1770 "3.5"
1771 );
1772 assert_eq!(
1773 POFloat::get_display(&context.get_property::<_, POFloat>(none_person)),
1774 "None"
1775 );
1776
1777 assert_eq!(
1778 POu32Custom::get_display(&context.get_property::<_, POu32Custom>(some_person)),
1779 "custom:7"
1780 );
1781 assert_eq!(
1782 POu32Custom::get_display(&context.get_property::<_, POu32Custom>(none_person)),
1783 "custom:none"
1784 );
1785 }
1786
1787 #[test]
1788 fn test_option_derived_property_display_patterns() {
1789 let mut context = Context::new();
1790
1791 let some_person = context
1792 .add_entity::<Person, _>(with!(Person, Age(42)))
1793 .unwrap();
1794 let none_person = context
1795 .add_entity::<Person, _>(with!(Person, Age(0)))
1796 .unwrap();
1797
1798 assert_eq!(
1799 DerivedMaybeAge::get_display(&context.get_property::<_, DerivedMaybeAge>(some_person)),
1800 "42"
1801 );
1802 assert_eq!(
1803 DerivedMaybeAge::get_display(&context.get_property::<_, DerivedMaybeAge>(none_person)),
1804 "None"
1805 );
1806
1807 assert_eq!(
1808 DerivedMaybeWeight::get_display(
1809 &context.get_property::<_, DerivedMaybeWeight>(some_person)
1810 ),
1811 "42.0"
1812 );
1813 assert_eq!(
1814 DerivedMaybeWeight::get_display(
1815 &context.get_property::<_, DerivedMaybeWeight>(none_person)
1816 ),
1817 "None"
1818 );
1819
1820 assert_eq!(
1821 DerivedMaybeAgeCustom::get_display(
1822 &context.get_property::<_, DerivedMaybeAgeCustom>(some_person)
1823 ),
1824 "derived:42"
1825 );
1826 assert_eq!(
1827 DerivedMaybeAgeCustom::get_display(
1828 &context.get_property::<_, DerivedMaybeAgeCustom>(none_person)
1829 ),
1830 "derived:none"
1831 );
1832 }
1833
1834 #[test]
1835 fn test_debug_trait() {
1836 let property = Pu32(11);
1837 let debug_str = format!("{:?}", property);
1838 assert_eq!(debug_str, "Pu32(11)");
1839
1840 let property = POu32(Some(22));
1841 let debug_str = format!("{:?}", property);
1842 assert_eq!(debug_str, "POu32(Some(22))");
1843 }
1844
1845 #[test]
1846 fn test_define_derived_property_impl_eq_hash() {
1847 let mut values = crate::HashSet::default();
1848 values.insert(DerivedWeight(3.0));
1849 assert!(values.contains(&DerivedWeight(3.0)));
1850 }
1851}