1use crate::hashing::hash_serialized_128;
32use crate::people::data::PersonPropertyHolder;
33use crate::{Context, PersonId};
34use serde::Serialize;
35use std::any::TypeId;
36use std::fmt::Debug;
37
38pub trait PersonPropertyValue: Copy + Debug + PartialEq + Serialize {}
42impl<T> PersonPropertyValue for T where T: Copy + Debug + PartialEq + Serialize {}
43
44pub trait PersonProperty: Copy + 'static {
51 type Value: PersonPropertyValue;
53 type CanonicalValue: PersonPropertyValue;
56
57 #[must_use]
58 fn is_derived() -> bool {
59 false
60 }
61
62 #[must_use]
63 fn is_required() -> bool {
64 false
65 }
66
67 #[must_use]
68 fn dependencies() -> Vec<Box<dyn PersonPropertyHolder>> {
69 panic!("Dependencies not implemented");
70 }
71
72 fn register_dependencies(_: &Context) {
73 panic!("Dependencies not implemented");
74 }
75
76 fn compute(context: &Context, person_id: PersonId) -> Self::Value;
77
78 #[must_use]
81 fn make_canonical(value: Self::Value) -> Self::CanonicalValue;
82
83 #[must_use]
85 fn make_uncanonical(value: Self::CanonicalValue) -> Self::Value;
86 fn get_instance() -> Self;
87 fn name() -> &'static str;
88
89 #[must_use]
93 fn get_display(value: &Self::CanonicalValue) -> String;
94
95 #[must_use]
97 fn hash_property_value(value: &Self::CanonicalValue) -> u128 {
98 hash_serialized_128(value)
99 }
100
101 #[must_use]
104 fn type_id() -> TypeId {
105 TypeId::of::<Self>()
106 }
107}
108
109#[macro_export]
110macro_rules! __define_person_property_common {
111 ($person_property:ident, $value:ty, $compute_fn:expr, $is_required:expr, $display_impl:expr) => {
112 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
113 pub struct $person_property;
114 impl $crate::people::PersonProperty for $person_property {
115 type Value = $value;
116 type CanonicalValue = $value;
117 fn compute(
118 _context: &$crate::context::Context,
119 _person: $crate::people::PersonId,
120 ) -> Self::CanonicalValue {
121 $compute_fn(_context, _person)
122 }
123 fn make_canonical(value: Self::Value) -> Self::CanonicalValue {
124 value
125 }
126 fn make_uncanonical(value: Self::CanonicalValue) -> Self::Value {
127 value
128 }
129 fn is_required() -> bool {
130 $is_required
131 }
132 fn get_instance() -> Self {
133 $person_property
134 }
135 fn name() -> &'static str {
136 stringify!($person_property)
137 }
138 fn get_display(value: &Self::CanonicalValue) -> String {
139 $display_impl(value)
140 }
141 }
142 };
143}
144
145#[macro_export]
152macro_rules! define_person_property {
153 ($person_property:ident, Option<$value:ty>, $initialize:expr) => {
155 $crate::__define_person_property_common!(
156 $person_property,
157 Option<$value>,
158 $initialize,
159 false,
160 |&value| {
161 match value {
162 Some(v) => format!("{:?}", v),
163 None => "None".to_string(),
164 }
165 }
166 );
167 };
168 ($person_property:ident, $value:ty, $initialize:expr) => {
170 $crate::__define_person_property_common!(
171 $person_property,
172 $value,
173 $initialize,
174 false,
175 |&value| format!("{:?}", value)
176 );
177 };
178 ($person_property:ident, Option<$value:ty>) => {
180 $crate::__define_person_property_common!(
181 $person_property,
182 Option<$value>,
183 |_, _| panic!("Property not initialized when person created."),
184 true,
185 |&value| {
186 match value {
187 Some(v) => format!("{:?}", v),
188 None => "None".to_string(),
189 }
190 }
191 );
192 };
193 ($person_property:ident, $value:ty) => {
195 $crate::__define_person_property_common!(
196 $person_property,
197 $value,
198 |_, _| panic!("Property not initialized when person created."),
199 true,
200 |&value| format!("{:?}", value)
201 );
202 };
203}
204pub use define_person_property;
205
206#[macro_export]
211macro_rules! define_person_property_with_default {
212 ($person_property:ident, Option<$value:ty>, $default:expr) => {
213 $crate::define_person_property!(
214 $person_property,
215 Option<$value>,
216 |_context, _person_id| { $default }
217 );
218 };
219 ($person_property:ident, $value:ty, $default:expr) => {
220 $crate::define_person_property!($person_property, $value, |_context, _person_id| {
221 $default
222 });
223 };
224}
225pub use define_person_property_with_default;
226
227#[macro_export]
236macro_rules! __define_derived_property_common {
237 (
238 $derived_property:ident,
239 $value:ty,
240 $canonical_value:ty,
241 $compute_canonical_impl:expr,
242 $compute_uncanonical_impl:expr,
243 $at_dependency_registration:expr,
244 [$($dependency:ident),*],
245 [$($global_dependency:ident),*],
246 |$($param:ident),+| $derive_fn:expr,
247 $display_impl:expr,
248 $hash_fn:expr,
249 $type_id_impl:expr
250 ) => {
251 #[derive(Debug, Copy, Clone)]
252 pub struct $derived_property;
253
254 impl $crate::people::PersonProperty for $derived_property {
255 type Value = $value;
256 type CanonicalValue = $canonical_value;
257
258 fn compute(context: &$crate::context::Context, person_id: $crate::people::PersonId) -> Self::Value {
259 #[allow(unused_imports)]
260 use $crate::global_properties::ContextGlobalPropertiesExt;
261 #[allow(unused_parens)]
262 let ($($param,)*) = (
263 $(context.get_person_property(person_id, $dependency)),*,
264 $(
265 context.get_global_property_value($global_dependency)
266 .expect(&format!("Global property {} not initialized", stringify!($global_dependency)))
267 ),*
268 );
269 #[allow(non_snake_case)]
270 (|$($param),+| $derive_fn)($($param),+)
271 }
272 fn make_canonical(value: Self::Value) -> Self::CanonicalValue {
273 ($compute_canonical_impl)(value)
274 }
275 fn make_uncanonical(value: Self::CanonicalValue) -> Self::Value {
276 ($compute_uncanonical_impl)(value)
277 }
278 fn is_derived() -> bool { true }
279 fn dependencies() -> Vec<Box<dyn $crate::people::PersonPropertyHolder>> {
280 vec![$(
281 Box::new($dependency) as Box<dyn $crate::people::PersonPropertyHolder>
282 ),*]
283 }
284 fn register_dependencies(context: &$crate::context::Context) {
285 $at_dependency_registration
286 $(context.register_property::<$dependency>();)+
287 }
288 fn get_instance() -> Self {
289 $derived_property
290 }
291 fn name() -> &'static str {
292 stringify!($derived_property)
293 }
294 fn get_display(value: &Self::CanonicalValue) -> String {
295 $display_impl(value)
296 }
297 fn hash_property_value(value: &Self::CanonicalValue) -> u128 {
298 ($hash_fn)(value)
299 }
300 fn type_id() -> std::any::TypeId {
301 $type_id_impl
302 }
303 }
304 };
305}
306
307#[macro_export]
314macro_rules! define_derived_property {
315 (
316 $derived_property:ident,
317 $value:ty,
318 [$($dependency:ident),*],
319 [$($global_dependency:ident),*],
320 |$($param:ident),+| $derive_fn:expr
321 ) => {
322 $crate::__define_derived_property_common!(
323 $derived_property,
324 $value,
325 $value,
326 |v| v,
327 |v| v,
328 {},
329 [$($dependency),*],
330 [$($global_dependency),*],
331 |$($param),+| $derive_fn,
332 |&value| format!("{:?}", value),
333 $crate::hashing::hash_serialized_128,
334 std::any::TypeId::of::<Self>()
335 );
336 };
337
338 (
340 $derived_property:ident,
341 $value:ty,
342 [$($dependency:ident),*],
343 |$($param:ident),+| $derive_fn:expr
344 ) => {
345 $crate::__define_derived_property_common!(
346 $derived_property,
347 $value,
348 $value,
349 |v| v,
350 |v| v,
351 {},
352 [$($dependency),*],
353 [],
354 |$($param),+| $derive_fn,
355 |&value| format!("{:?}", value),
356 $crate::hashing::hash_serialized_128,
357 std::any::TypeId::of::<Self>()
358 );
359 };
360}
361pub use define_derived_property;
362
363#[macro_export]
364macro_rules! define_multi_property {
365 (
366 $person_property:ident,
367 ( $($dependency:ident),+ )
368 ) => {
369 $crate::paste::paste! {
371 $crate::__define_derived_property_common!(
372 $person_property,
374
375 ( $(<$dependency as $crate::people::PersonProperty>::Value),+ ),
377
378 $crate::sorted_value_type!(( $($dependency),+ )),
380
381 $person_property::reorder_by_tag,
383
384 $person_property::unreorder_by_tag,
386
387 {
389 let type_ids = &mut [$($dependency::type_id()),+ ];
390 type_ids.sort();
391 $crate::people::register_type_ids_to_muli_property_id(type_ids, Self::type_id());
392 },
393
394 [$($dependency),+],
396
397 [],
399
400 |$( [<_ $dependency:lower>] ),+| {
402 ( $( [<_ $dependency:lower>] ),+ )
403 },
404
405 |values_tuple: &Self::CanonicalValue| {
407 let values_tuple: Self::Value = Self::unreorder_by_tag(*values_tuple);
409 let mut displayed = String::from("(");
410 let ( $( [<_ $dependency:lower>] ),+ ) = values_tuple;
411 $(
412 displayed.push_str(<$dependency as $crate::PersonProperty>::get_display(
413 & <$dependency as $crate::PersonProperty>::make_canonical([<_ $dependency:lower>])
414 ).as_str());
415 displayed.push_str(", ");
416 )+
417 displayed.truncate(displayed.len() - 2);
418 displayed.push_str(")");
419 displayed
420 },
421
422 $crate::hashing::hash_serialized_128,
424
425 std::any::TypeId::of::<$crate::sorted_tag!(( $($dependency),+ ))>()
430 );
431 $crate::impl_make_canonical!($person_property, ( $($dependency),+ ));
432 }
433 };
434}
435pub use define_multi_property;
436
437#[cfg(test)]
438mod tests {
439 use super::*;
440 use crate::people::{PeoplePlugin, Query};
441 use crate::prelude::*;
442 use crate::PersonProperty;
443
444 define_person_property!(Pu32, u32);
445 define_person_property!(POu32, Option<u32>);
446
447 define_person_property!(Name, &'static str);
448 define_person_property!(Age, u8);
449 define_person_property!(Weight, f64);
450
451 define_multi_property!(ProfileNAW, (Name, Age, Weight));
452 define_multi_property!(ProfileAWN, (Age, Weight, Name));
453 define_multi_property!(ProfileWAN, (Weight, Age, Name));
454
455 #[test]
456 fn test_multi_property_ordering() {
457 let a: <ProfileNAW as PersonProperty>::Value = ("Jane", 22, 180.5);
458 let b: <ProfileAWN as PersonProperty>::Value = (22, 180.5, "Jane");
459 let c: <ProfileWAN as PersonProperty>::Value = (180.5, 22, "Jane");
460
461 assert_eq!(ProfileNAW::type_id(), ProfileAWN::type_id());
462 assert_eq!(ProfileNAW::type_id(), ProfileWAN::type_id());
463
464 let a_canonical: <ProfileNAW as PersonProperty>::CanonicalValue =
465 ProfileNAW::make_canonical(a);
466 let b_canonical: <ProfileAWN as PersonProperty>::CanonicalValue =
467 ProfileAWN::make_canonical(b);
468 let c_canonical: <ProfileWAN as PersonProperty>::CanonicalValue =
469 ProfileWAN::make_canonical(c);
470
471 assert_eq!(a_canonical, b_canonical);
472 assert_eq!(a_canonical, c_canonical);
473
474 assert_eq!(
477 ProfileNAW::hash_property_value(&a_canonical),
478 ProfileAWN::hash_property_value(&b_canonical)
479 );
480 assert_eq!(
481 ProfileNAW::hash_property_value(&a_canonical),
482 ProfileWAN::hash_property_value(&c_canonical)
483 );
484
485 assert_eq!(ProfileNAW::make_uncanonical(b_canonical), a);
488 assert_eq!(ProfileAWN::make_uncanonical(c_canonical), b);
489 assert_eq!(ProfileWAN::make_uncanonical(a_canonical), c);
490 }
491
492 #[test]
493 fn test_multi_property_vs_property_query() {
494 let mut context = Context::new();
495
496 context
497 .add_person(((Name, "John"), (Age, 42), (Weight, 220.5)))
498 .unwrap();
499 context
500 .add_person(((Name, "Jane"), (Age, 22), (Weight, 180.5)))
501 .unwrap();
502 context
503 .add_person(((Name, "Bob"), (Age, 32), (Weight, 190.5)))
504 .unwrap();
505 context
506 .add_person(((Name, "Alice"), (Age, 22), (Weight, 170.5)))
507 .unwrap();
508
509 context.index_property(ProfileNAW);
510
511 {
512 let data = context.get_data(PeoplePlugin);
513 assert!(data
514 .property_indexes
515 .borrow()
516 .get(&ProfileNAW::type_id())
517 .is_some());
518 }
519
520 {
521 let example_query = ((Name, "Alice"), (Age, 22), (Weight, 170.5));
522 let query_multi_property_type_id = Query::multi_property_type_id(&example_query);
523 assert!(query_multi_property_type_id.is_some());
524 assert_eq!(ProfileNAW::type_id(), query_multi_property_type_id.unwrap());
525 assert_eq!(
526 Query::multi_property_value_hash(&example_query),
527 ProfileNAW::hash_property_value(&ProfileNAW::make_canonical(("Alice", 22, 170.5)))
528 );
529 }
530
531 context.with_query_results((ProfileNAW, ("John", 42, 220.5)), &mut |results| {
532 assert_eq!(results.len(), 1);
533 });
534 }
535
536 #[test]
537 fn test_get_display() {
538 let mut context = Context::new();
539 let person = context.add_person(((POu32, Some(42)), (Pu32, 22))).unwrap();
540 assert_eq!(
541 format!(
542 "{:}",
543 POu32::get_display(&context.get_person_property(person, POu32))
544 ),
545 "42"
546 );
547 assert_eq!(
548 format!(
549 "{:}",
550 Pu32::get_display(&context.get_person_property(person, Pu32))
551 ),
552 "22"
553 );
554 let person2 = context.add_person(((POu32, None), (Pu32, 11))).unwrap();
555 assert_eq!(
556 format!(
557 "{:}",
558 POu32::get_display(&context.get_person_property(person2, POu32))
559 ),
560 "None"
561 );
562 }
563
564 #[test]
565 fn test_debug_trait() {
566 let property = Pu32;
567 let debug_str = format!("{:?}", property);
568 assert_eq!(debug_str, "Pu32");
569
570 let property = POu32;
571 let debug_str = format!("{:?}", property);
572 assert_eq!(debug_str, "POu32");
573 }
574}