1use std::any::TypeId;
32use std::fmt::Debug;
33
34use serde::Serialize;
35
36use crate::hashing::hash_serialized_128;
37use crate::people::data::PersonPropertyHolder;
38use crate::{Context, PersonId};
39
40pub trait PersonPropertyValue: Copy + Debug + PartialEq + Serialize {}
44impl<T> PersonPropertyValue for T where T: Copy + Debug + PartialEq + Serialize {}
45
46pub trait PersonProperty: Copy + 'static {
53 type Value: PersonPropertyValue;
55 type CanonicalValue: PersonPropertyValue;
58
59 #[must_use]
60 fn is_derived() -> bool {
61 false
62 }
63
64 #[must_use]
65 fn is_required() -> bool {
66 false
67 }
68
69 #[must_use]
70 fn dependencies() -> Vec<Box<dyn PersonPropertyHolder>> {
71 panic!("Dependencies not implemented");
72 }
73
74 fn register_dependencies(_: &Context) {
75 panic!("Dependencies not implemented");
76 }
77
78 fn compute(context: &Context, person_id: PersonId) -> Self::Value;
79
80 #[must_use]
83 fn make_canonical(value: Self::Value) -> Self::CanonicalValue;
84
85 #[must_use]
87 fn make_uncanonical(value: Self::CanonicalValue) -> Self::Value;
88 fn get_instance() -> Self;
89 fn name() -> &'static str;
90
91 #[must_use]
95 fn get_display(value: &Self::CanonicalValue) -> String;
96
97 #[must_use]
99 fn hash_property_value(value: &Self::CanonicalValue) -> u128 {
100 hash_serialized_128(value)
101 }
102
103 #[must_use]
106 fn type_id() -> TypeId {
107 TypeId::of::<Self>()
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use crate::people::{PeoplePlugin, Query};
115 use crate::prelude::*;
116 use crate::PersonProperty;
117
118 define_person_property!(Pu32, u32);
119 define_person_property!(POu32, Option<u32>);
120
121 define_person_property!(Name, &'static str);
122 define_person_property!(Age, u8);
123 define_person_property!(Weight, f64);
124
125 define_multi_property!(ProfileNAW, (Name, Age, Weight));
126 define_multi_property!(ProfileAWN, (Age, Weight, Name));
127 define_multi_property!(ProfileWAN, (Weight, Age, Name));
128
129 #[test]
130 fn test_multi_property_ordering() {
131 let a: <ProfileNAW as PersonProperty>::Value = ("Jane", 22, 180.5);
132 let b: <ProfileAWN as PersonProperty>::Value = (22, 180.5, "Jane");
133 let c: <ProfileWAN as PersonProperty>::Value = (180.5, 22, "Jane");
134
135 assert_eq!(ProfileNAW::type_id(), ProfileAWN::type_id());
136 assert_eq!(ProfileNAW::type_id(), ProfileWAN::type_id());
137
138 let a_canonical: <ProfileNAW as PersonProperty>::CanonicalValue =
139 ProfileNAW::make_canonical(a);
140 let b_canonical: <ProfileAWN as PersonProperty>::CanonicalValue =
141 ProfileAWN::make_canonical(b);
142 let c_canonical: <ProfileWAN as PersonProperty>::CanonicalValue =
143 ProfileWAN::make_canonical(c);
144
145 assert_eq!(a_canonical, b_canonical);
146 assert_eq!(a_canonical, c_canonical);
147
148 assert_eq!(
151 ProfileNAW::hash_property_value(&a_canonical),
152 ProfileAWN::hash_property_value(&b_canonical)
153 );
154 assert_eq!(
155 ProfileNAW::hash_property_value(&a_canonical),
156 ProfileWAN::hash_property_value(&c_canonical)
157 );
158
159 assert_eq!(ProfileNAW::make_uncanonical(b_canonical), a);
162 assert_eq!(ProfileAWN::make_uncanonical(c_canonical), b);
163 assert_eq!(ProfileWAN::make_uncanonical(a_canonical), c);
164 }
165
166 #[test]
167 fn test_multi_property_vs_property_query() {
168 let mut context = Context::new();
169
170 context
171 .add_person(((Name, "John"), (Age, 42), (Weight, 220.5)))
172 .unwrap();
173 context
174 .add_person(((Name, "Jane"), (Age, 22), (Weight, 180.5)))
175 .unwrap();
176 context
177 .add_person(((Name, "Bob"), (Age, 32), (Weight, 190.5)))
178 .unwrap();
179 context
180 .add_person(((Name, "Alice"), (Age, 22), (Weight, 170.5)))
181 .unwrap();
182
183 context.index_property(ProfileNAW);
184
185 {
186 let data = context.get_data(PeoplePlugin);
187 assert!(data
188 .property_indexes
189 .borrow()
190 .get(&ProfileNAW::type_id())
191 .is_some());
192 }
193
194 {
195 let example_query = ((Name, "Alice"), (Age, 22), (Weight, 170.5));
196 let query_multi_property_type_id = Query::multi_property_type_id(&example_query);
197 assert!(query_multi_property_type_id.is_some());
198 assert_eq!(ProfileNAW::type_id(), query_multi_property_type_id.unwrap());
199 assert_eq!(
200 Query::multi_property_value_hash(&example_query),
201 ProfileNAW::hash_property_value(&ProfileNAW::make_canonical(("Alice", 22, 170.5)))
202 );
203 }
204
205 context.with_query_results((ProfileNAW, ("John", 42, 220.5)), &mut |results| {
206 assert_eq!(results.len(), 1);
207 });
208 }
209
210 #[test]
211 fn test_get_display() {
212 let mut context = Context::new();
213 let person = context.add_person(((POu32, Some(42)), (Pu32, 22))).unwrap();
214 assert_eq!(
215 format!(
216 "{:}",
217 POu32::get_display(&context.get_person_property(person, POu32))
218 ),
219 "42"
220 );
221 assert_eq!(
222 format!(
223 "{:}",
224 Pu32::get_display(&context.get_person_property(person, Pu32))
225 ),
226 "22"
227 );
228 let person2 = context.add_person(((POu32, None), (Pu32, 11))).unwrap();
229 assert_eq!(
230 format!(
231 "{:}",
232 POu32::get_display(&context.get_person_property(person2, POu32))
233 ),
234 "None"
235 );
236 }
237
238 #[test]
239 fn test_debug_trait() {
240 let property = Pu32;
241 let debug_str = format!("{:?}", property);
242 assert_eq!(debug_str, "Pu32");
243
244 let property = POu32;
245 let debug_str = format!("{:?}", property);
246 assert_eq!(debug_str, "POu32");
247 }
248}