ixa/people/
macros.rs

1#[macro_export]
2macro_rules! __define_person_property_common {
3    ($person_property:ident, $value:ty, $compute_fn:expr, $is_required:expr, $display_impl:expr) => {
4        #[derive(Debug, Copy, Clone, Eq, PartialEq)]
5        pub struct $person_property;
6        impl $crate::people::PersonProperty for $person_property {
7            type Value = $value;
8            type CanonicalValue = $value;
9            fn compute(
10                _context: &$crate::context::Context,
11                _person: $crate::people::PersonId,
12            ) -> Self::CanonicalValue {
13                $compute_fn(_context, _person)
14            }
15            fn make_canonical(value: Self::Value) -> Self::CanonicalValue {
16                value
17            }
18            fn make_uncanonical(value: Self::CanonicalValue) -> Self::Value {
19                value
20            }
21            fn is_required() -> bool {
22                $is_required
23            }
24            fn get_instance() -> Self {
25                $person_property
26            }
27            fn name() -> &'static str {
28                stringify!($person_property)
29            }
30            fn get_display(value: &Self::CanonicalValue) -> String {
31                $display_impl(value)
32            }
33        }
34    };
35}
36
37/// Defines a person property with the following parameters:
38/// * `$person_property`: A name for the identifier type of the property
39/// * `$value`: The type of the property's value
40/// * `$initialize`: (Optional) A function that takes a `Context` and `PersonId` and
41///   returns the initial value. If it is not defined, calling `get_person_property`
42///   on the property without explicitly setting a value first will panic.
43#[macro_export]
44macro_rules! define_person_property {
45    // Option<T> with initializer
46    ($person_property:ident, Option<$value:ty>, $initialize:expr) => {
47        $crate::__define_person_property_common!(
48            $person_property,
49            Option<$value>,
50            $initialize,
51            false,
52            |&value| {
53                match value {
54                    Some(v) => format!("{:?}", v),
55                    None => "None".to_string(),
56                }
57            }
58        );
59    };
60    // T with initializer
61    ($person_property:ident, $value:ty, $initialize:expr) => {
62        $crate::__define_person_property_common!(
63            $person_property,
64            $value,
65            $initialize,
66            false,
67            |&value| format!("{:?}", value)
68        );
69    };
70    // Option<T> without initializer
71    ($person_property:ident, Option<$value:ty>) => {
72        $crate::__define_person_property_common!(
73            $person_property,
74            Option<$value>,
75            |_, _| panic!("Property not initialized when person created."),
76            true,
77            |&value| {
78                match value {
79                    Some(v) => format!("{:?}", v),
80                    None => "None".to_string(),
81                }
82            }
83        );
84    };
85    // T without initializer
86    ($person_property:ident, $value:ty) => {
87        $crate::__define_person_property_common!(
88            $person_property,
89            $value,
90            |_, _| panic!("Property not initialized when person created."),
91            true,
92            |&value| format!("{:?}", value)
93        );
94    };
95}
96pub use define_person_property;
97
98/// Defines a person property with the following parameters:
99/// * `$person_property`: A name for the identifier type of the property
100/// * `$value`: The type of the property's value
101/// * `$default`: An initial value
102#[macro_export]
103macro_rules! define_person_property_with_default {
104    ($person_property:ident, Option<$value:ty>, $default:expr) => {
105        $crate::define_person_property!(
106            $person_property,
107            Option<$value>,
108            |_context, _person_id| { $default }
109        );
110    };
111    ($person_property:ident, $value:ty, $default:expr) => {
112        $crate::define_person_property!($person_property, $value, |_context, _person_id| {
113            $default
114        });
115    };
116}
117pub use define_person_property_with_default;
118
119/// Defines a derived person property with the following parameters:
120/// * `$person_property`: A name for the identifier type of the property
121/// * `$value`: The type of the property's value
122/// * `[$($dependency),+]`: A list of person properties the derived property depends on
123/// * `[$($dependency),*]`: A list of global properties the derived property depends on (optional)
124/// * `$calculate`: A closure that takes the values of each dependency and returns the derived value
125/// * `$display`: A closure that takes the value of the derived property and returns a string representation
126/// * `$hash_fn`: A function that can compute the hash of values of this property
127#[macro_export]
128macro_rules! __define_derived_property_common {
129    (
130        $derived_property:ident,
131        $value:ty,
132        $canonical_value:ty,
133        $compute_canonical_impl:expr,
134        $compute_uncanonical_impl:expr,
135        $at_dependency_registration:expr,
136        [$($dependency:ident),*],
137        [$($global_dependency:ident),*],
138        |$($param:ident),+| $derive_fn:expr,
139        $display_impl:expr,
140        $hash_fn:expr,
141        $type_id_impl:expr
142    ) => {
143        #[derive(Debug, Copy, Clone)]
144        pub struct $derived_property;
145
146        impl $crate::people::PersonProperty for $derived_property {
147            type Value = $value;
148            type CanonicalValue = $canonical_value;
149
150            fn compute(context: &$crate::context::Context, person_id: $crate::people::PersonId) -> Self::Value {
151                #[allow(unused_imports)]
152                use $crate::global_properties::ContextGlobalPropertiesExt;
153                #[allow(unused_parens)]
154                let ($($param,)*) = (
155                    $(context.get_person_property(person_id, $dependency)),*,
156                    $(
157                        context.get_global_property_value($global_dependency)
158                            .expect(&format!("Global property {} not initialized", stringify!($global_dependency)))
159                    ),*
160                );
161                #[allow(non_snake_case)]
162                (|$($param),+| $derive_fn)($($param),+)
163            }
164            fn make_canonical(value: Self::Value) -> Self::CanonicalValue {
165                ($compute_canonical_impl)(value)
166            }
167            fn make_uncanonical(value: Self::CanonicalValue) -> Self::Value {
168                ($compute_uncanonical_impl)(value)
169            }
170            fn is_derived() -> bool { true }
171            fn dependencies() -> Vec<Box<dyn $crate::people::PersonPropertyHolder>> {
172                vec![$(
173                    Box::new($dependency) as Box<dyn $crate::people::PersonPropertyHolder>
174                ),*]
175            }
176            fn register_dependencies(context: &$crate::context::Context) {
177                $at_dependency_registration
178                $(context.register_property::<$dependency>();)+
179            }
180            fn get_instance() -> Self {
181                $derived_property
182            }
183            fn name() -> &'static str {
184                stringify!($derived_property)
185            }
186            fn get_display(value: &Self::CanonicalValue) -> String {
187                $display_impl(value)
188            }
189            fn hash_property_value(value: &Self::CanonicalValue) -> u128 {
190                ($hash_fn)(value)
191            }
192            fn type_id() -> std::any::TypeId {
193                $type_id_impl
194            }
195        }
196    };
197}
198
199/// Defines a derived person property with the following parameters:
200/// * `$person_property`: A name for the identifier type of the property
201/// * `$value`: The type of the property's value
202/// * `[$($dependency),+]`: A list of person properties the derived property depends on
203/// * `[$($dependency),*]`: A list of global properties the derived property depends on (optional)
204/// * $calculate: A closure that takes the values of each dependency and returns the derived value
205#[macro_export]
206macro_rules! define_derived_property {
207    (
208        $derived_property:ident,
209        $value:ty,
210        [$($dependency:ident),*],
211        [$($global_dependency:ident),*],
212        |$($param:ident),+| $derive_fn:expr
213    ) => {
214        $crate::__define_derived_property_common!(
215            $derived_property,
216            $value,
217            $value,
218            |v| v,
219            |v| v,
220            {/* empty*/},
221            [$($dependency),*],
222            [$($global_dependency),*],
223            |$($param),+| $derive_fn,
224            |&value| format!("{:?}", value),
225            $crate::hashing::hash_serialized_128,
226            std::any::TypeId::of::<Self>()
227        );
228    };
229
230    // Empty global dependencies
231    (
232        $derived_property:ident,
233        $value:ty,
234        [$($dependency:ident),*],
235        |$($param:ident),+| $derive_fn:expr
236    ) => {
237        $crate::__define_derived_property_common!(
238            $derived_property,
239            $value,
240            $value,
241            |v| v,
242            |v| v,
243            {/* empty*/},
244            [$($dependency),*],
245            [],
246            |$($param),+| $derive_fn,
247            |&value| format!("{:?}", value),
248            $crate::hashing::hash_serialized_128,
249            std::any::TypeId::of::<Self>()
250        );
251    };
252}
253pub use define_derived_property;
254
255/// Defines a named multi-property composed of a tuple of several existing other properties.  
256/// - `$person_property`: The name of the new multi-property type.  
257/// - `( $($dependency),+ )`: A non-empty, comma-separated, ordered list of existing  
258///   property identifiers that this multi-property is composed from.  
259#[macro_export]
260macro_rules! define_multi_property {
261    (
262        $person_property:ident,
263        ( $($dependency:ident),+ )
264    ) => {
265        // $crate::sorted_property_impl!(( $($dependency),+ ));
266        $crate::paste::paste! {
267            $crate::__define_derived_property_common!(
268                // Name
269                $person_property,
270
271                // `PersonProperty::Value` type
272                ( $(<$dependency as $crate::people::PersonProperty>::Value),+ ),
273
274                // `PersonProperty::CanonicalValue` type
275                $crate::sorted_value_type!(( $($dependency),+ )),
276
277                // Function to transform a `PersonProperty::Value` to a `PersonProperty::CanonicalValue`
278                $person_property::reorder_by_tag,
279
280                // Function to transform a `PersonProperty::CanonicalValue` to a `PersonProperty::Value`
281                $person_property::unreorder_by_tag,
282
283                // Code that runs at dependency registration time
284                {
285                    let type_ids = &mut [$($dependency::type_id()),+ ];
286                    type_ids.sort();
287                    $crate::people::register_type_ids_to_muli_property_id(type_ids, Self::type_id());
288                },
289
290                // Property dependency list
291                [$($dependency),+],
292
293                // Global property dependency list
294                [],
295
296                // A function that takes the values of each dependency and returns the derived value
297                |$( [<_ $dependency:lower>] ),+| {
298                    ( $( [<_ $dependency:lower>] ),+ )
299                },
300
301                // A function that takes a canonical value and returns a string representation of it.
302                |values_tuple: &Self::CanonicalValue| {
303                    // ice tThe string representation uses the original (unsorted) ordering.
304                    let values_tuple: Self::Value = Self::unreorder_by_tag(*values_tuple);
305                    let mut displayed = String::from("(");
306                    let ( $( [<_ $dependency:lower>] ),+ ) = values_tuple;
307                    $(
308                        displayed.push_str(<$dependency as $crate::PersonProperty>::get_display(
309                            & <$dependency as $crate::PersonProperty>::make_canonical([<_ $dependency:lower>])
310                        ).as_str());
311                        displayed.push_str(", ");
312                    )+
313                    displayed.truncate(displayed.len() - 2);
314                    displayed.push_str(")");
315                    displayed
316                },
317
318                // A function that computes the hash of a value of this property
319                $crate::hashing::hash_serialized_128,
320
321                // The Type ID of the property.
322                // The type ID of a multi-property is the type ID of the SORTED tuple of its
323                // components. This is so that tuples with the same component types in a different
324                // order will have the same type ID.
325                std::any::TypeId::of::<$crate::sorted_tag!(( $($dependency),+ ))>()
326            );
327            $crate::impl_make_canonical!($person_property, ( $($dependency),+ ));
328        }
329    };
330}
331pub use define_multi_property;