1use crate::people::data::PersonPropertyHolder;
2use crate::{Context, PersonId};
3use serde::Serialize;
4use std::fmt::Debug;
5
6pub trait PersonProperty: Copy + 'static {
13 type Value: Copy + Debug + PartialEq + Serialize;
14 #[must_use]
15 fn is_derived() -> bool {
16 false
17 }
18 #[must_use]
19 fn is_required() -> bool {
20 false
21 }
22 #[must_use]
23 fn dependencies() -> Vec<Box<dyn PersonPropertyHolder>> {
24 panic!("Dependencies not implemented");
25 }
26 fn register_dependencies(_: &Context) {
27 panic!("Dependencies not implemented");
28 }
29 fn compute(context: &Context, person_id: PersonId) -> Self::Value;
30 fn get_instance() -> Self;
31 fn name() -> &'static str;
32 fn get_display(value: &Self::Value) -> String;
33}
34
35#[macro_export]
36macro_rules! __define_person_property_common {
37 ($person_property:ident, $value:ty, $compute_fn:expr, $is_required:expr, $display_impl:expr) => {
38 #[derive(Debug, Copy, Clone)]
39 pub struct $person_property;
40 impl $crate::people::PersonProperty for $person_property {
41 type Value = $value;
42 fn compute(
43 _context: &$crate::context::Context,
44 _person: $crate::people::PersonId,
45 ) -> Self::Value {
46 $compute_fn(_context, _person)
47 }
48 fn is_required() -> bool {
49 $is_required
50 }
51 fn get_instance() -> Self {
52 $person_property
53 }
54 fn name() -> &'static str {
55 stringify!($person_property)
56 }
57 fn get_display(value: &Self::Value) -> String {
58 $display_impl(value)
59 }
60 }
61 };
62}
63
64#[macro_export]
71macro_rules! define_person_property {
72 ($person_property:ident, Option<$value:ty>, $initialize:expr) => {
74 $crate::__define_person_property_common!(
75 $person_property,
76 Option<$value>,
77 $initialize,
78 false,
79 |&value| {
80 match value {
81 Some(v) => format!("{:?}", v),
82 None => "None".to_string(),
83 }
84 }
85 );
86 };
87 ($person_property:ident, $value:ty, $initialize:expr) => {
89 $crate::__define_person_property_common!(
90 $person_property,
91 $value,
92 $initialize,
93 false,
94 |&value| format!("{:?}", value)
95 );
96 };
97 ($person_property:ident, Option<$value:ty>) => {
99 $crate::__define_person_property_common!(
100 $person_property,
101 Option<$value>,
102 |_, _| panic!("Property not initialized when person created."),
103 true,
104 |&value| {
105 match value {
106 Some(v) => format!("{:?}", v),
107 None => "None".to_string(),
108 }
109 }
110 );
111 };
112 ($person_property:ident, $value:ty) => {
114 $crate::__define_person_property_common!(
115 $person_property,
116 $value,
117 |_, _| panic!("Property not initialized when person created."),
118 true,
119 |&value| format!("{:?}", value)
120 );
121 };
122}
123pub use define_person_property;
124
125#[macro_export]
130macro_rules! define_person_property_with_default {
131 ($person_property:ident, Option<$value:ty>, $default:expr) => {
132 $crate::define_person_property!(
133 $person_property,
134 Option<$value>,
135 |_context, _person_id| { $default }
136 );
137 };
138 ($person_property:ident, $value:ty, $default:expr) => {
139 $crate::define_person_property!($person_property, $value, |_context, _person_id| {
140 $default
141 });
142 };
143}
144pub use define_person_property_with_default;
145
146#[macro_export]
153macro_rules! define_derived_property {
154 (
155 $derived_property:ident,
156 $value:ty,
157 [$($dependency:ident),*],
158 [$($global_dependency:ident),*],
159 |$($param:ident),+| $derive_fn:expr
160 ) => {
161 #[derive(Debug, Copy, Clone)]
162 pub struct $derived_property;
163
164 impl $crate::people::PersonProperty for $derived_property {
165 type Value = $value;
166 fn compute(context: &$crate::context::Context, person_id: $crate::people::PersonId) -> Self::Value {
167 #[allow(unused_imports)]
168 use $crate::global_properties::ContextGlobalPropertiesExt;
169 #[allow(unused_parens)]
170 let ($($param,)*) = (
171 $(context.get_person_property(person_id, $dependency)),*,
172 $(
173 *context.get_global_property_value($global_dependency)
174 .expect(&format!("Global property {} not initialized", stringify!($global_dependency)))
175 ),*
176 );
177 (|$($param),+| $derive_fn)($($param),+)
178 }
179 fn is_derived() -> bool { true }
180 fn dependencies() -> Vec<Box<dyn $crate::people::PersonPropertyHolder>> {
181 vec![$(Box::new($dependency)),+]
182 }
183 fn register_dependencies(context: &$crate::context::Context) {
184 $(context.register_property::<$dependency>();)+
185 }
186 fn get_instance() -> Self {
187 $derived_property
188 }
189 fn name() -> &'static str {
190 stringify!($derived_property)
191 }
192 fn get_display(value: &Self::Value) -> String {
193 format!("{:?}", value)
194 }
195 }
196 };
197 (
198 $derived_property:ident,
199 $value:ty,
200 [$($dependency:ident),*],
201 |$($param:ident),+| $derive_fn:expr
202 ) => {
203 define_derived_property!(
204 $derived_property,
205 $value,
206 [$($dependency),*],
207 [],
208 |$($param),+| $derive_fn
209 );
210 };
211}
212pub use define_derived_property;
213
214#[macro_export]
215macro_rules! define_multi_property_index {
216 (
217 $($dependency:ident),+
218 ) => {
219 $crate::paste::paste! {
220 define_derived_property!(
221 [< $($dependency)+ Query >],
222 $crate::people::index::IndexValue,
223 [$($dependency),+],
224 |$([< $dependency:lower >]),+| {
225 let mut combined = vec!(
226 $(
227 (std::any::TypeId::of::<$dependency>(),
228 $crate::people::index::IndexValue::compute(&[< $dependency:lower >]))
229 ),*
230 );
231 combined.sort_by(|a, b| a.0.cmp(&b.0));
232 let values = combined.iter().map(|x| x.1).collect::<Vec<_>>();
233 $crate::people::index::IndexValue::compute(&values)
234 }
235 );
236
237 $crate::people::index::add_multi_property_index::<[< $($dependency)+ Query >]>(
238 #[allow(clippy::useless_vec)]
239 &vec![
240 $(
241 std::any::TypeId::of::<$dependency>(),
242 )*
243 ],
244 std::any::TypeId::of::<[< $($dependency)+ Query >]>(),
245 );
246 }
247 };
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253 use crate::prelude::*;
254
255 define_person_property!(Pu32, u32);
256 define_person_property!(POu32, Option<u32>);
257
258 #[test]
259 fn test_get_display() {
260 let mut context = Context::new();
261 let person = context.add_person(((POu32, Some(42)), (Pu32, 22))).unwrap();
262 assert_eq!(
263 format!(
264 "{:}",
265 POu32::get_display(&context.get_person_property(person, POu32))
266 ),
267 "42"
268 );
269 assert_eq!(
270 format!(
271 "{:}",
272 Pu32::get_display(&context.get_person_property(person, Pu32))
273 ),
274 "22"
275 );
276 let person2 = context.add_person(((POu32, None), (Pu32, 11))).unwrap();
277 assert_eq!(
278 format!(
279 "{:}",
280 POu32::get_display(&context.get_person_property(person2, POu32))
281 ),
282 "None"
283 );
284 }
285
286 #[test]
287 fn test_debug_trait() {
288 let value = Pu32;
289 let debug_str = format!("{:?}", value);
290 assert!(debug_str.contains("Pu32"));
292 let value = POu32;
293 let debug_str = format!("{:?}", value);
294 assert!(debug_str.contains("POu32"));
296 }
297}