ixa/people/
mod.rs

1//! A generic mechanism for representing people and associated data.
2//!
3//! We have a set of people indexed by [`PersonId`] and then each person
4//! can have an arbitrary number of person properties
5//! [`PersonProperty`], which are values keyed by a type. Person
6//! properties are defined with a macro ([`define_person_property!()`]
7//! or [`define_person_property_with_default!()`])
8//!
9//! # Initializing Person Properties
10//!
11//! Person properties can have their initial values set in several ways:
12//!
13//! * An initial value can be provided at person creation time in
14//!   [`Context::add_person()`].
15//! * The property can have a default value (provided when the
16//!   property is defined.)
17//! * The property can have an initializer function (provided when
18//!   the property is defined) that is called lazily when the
19//!   property is first accessed.
20//!
21//! If neither a default or an initializer is provided, then you
22//! must provide an initial value for each person on person
23//! creation. Failure to do so will generally cause failure of
24//! [`Context::add_person()`].
25//!
26//! # Setting Person Properties
27//!
28//! Properties can also have their values changed with [`Context::set_person_property()`].
29//! If the property is not initialized yet, this will implicitly call the
30//! initializer (or set the default) and then reset the value.
31//!
32//! # Derived Properties
33//!
34//! It is also possible to have a "derived property" whose value is
35//! computed based on a set of other properties. When a derived
36//! property is defined, you must supply a function that takes the
37//! values for those dependencies and computes the current value
38//! of the property. Note that this function cannot access the context
39//! directly and therefore cannot read any other properties. It also
40//! should have a consistent result for any set of inputs, because
41//! it may be called multiple times with those inputs, depending
42//! on the program structure.
43//!
44//! # Change Events
45//!
46//! Whenever a person property `E` has potentially changed, either
47//! because it was set directly or because it is a derived property
48//! and one of its dependencies changed, a
49//! [`PersonPropertyChangeEvent<E>`] will be emitted. Note that Ixa does
50//! not currently check that the new value is actually different from the old value,
51//! so calling [`Context::set_person_property()`] will always emit an event.
52//! Initialization is not considered a change, but [`Context::set_person_property()`]
53//! on a lazily initialized event will emit an event for the change from
54//! the initialized value to the new value.
55//!
56//! # Querying
57//!
58//! Person properties provides an interface to query for people matching
59//! a given set of properties. The basic syntax is to supply a set of
60//! (property, value) pairs, like so `query_people(((Age, 30), (Gender, Female)))`.
61//! Note that these need to be wrapped in an extra set of parentheses
62//! to make them a single tuple to pass to [`Context::query_people()`]. Queries implement
63//! strict equality, so if you want a fancier predicate you need to implement
64//! a derived property that computes it and then query over the derived property.
65//!
66//! The internals of query are deliberately opaque in that Ixa may or
67//! may not ordinarily choose to create caches or indexes for
68//! queries. However, you force an index to be created for a single
69//! property by using [`Context::index_property()`].
70
71mod context_extension;
72mod data;
73mod event;
74pub(crate) mod external_api;
75mod index;
76pub(crate) mod methods;
77mod multi_property;
78mod property;
79mod query;
80
81pub use query::Query;
82
83use crate::{context::Context, define_data_plugin};
84use crate::{HashMap, HashMapExt, HashSet, HashSetExt};
85pub use context_extension::ContextPeopleExt;
86use data::PeopleData;
87pub use data::PersonPropertyHolder;
88pub use event::{PersonCreatedEvent, PersonPropertyChangeEvent};
89pub use index::Index;
90pub use multi_property::*;
91pub use property::{
92    define_derived_property, define_multi_property, define_person_property,
93    define_person_property_with_default, PersonProperty,
94};
95
96use seq_macro::seq;
97use serde::{Deserialize, Serialize};
98use std::cell::RefCell;
99use std::fmt::{Debug, Display, Formatter};
100use std::{any::TypeId, hash::Hash};
101
102pub type HashValueType = u128;
103
104define_data_plugin!(
105    PeoplePlugin,
106    PeopleData,
107    PeopleData {
108        is_initializing: false,
109        current_population: 0,
110        methods: RefCell::new(HashMap::new()),
111        properties_map: RefCell::new(HashMap::new()),
112        registered_properties: RefCell::new(HashSet::new()),
113        dependency_map: RefCell::new(HashMap::new()),
114        property_indexes: RefCell::new(HashMap::new()),
115        people_types: RefCell::new(HashMap::new()),
116    }
117);
118
119/// Represents a unique person.
120//  The id refers to that person's index in the range 0 to population
121// - 1 in the PeopleData container.
122#[derive(Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
123pub struct PersonId(pub(crate) usize);
124
125impl Display for PersonId {
126    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
127        write!(f, "{}", self.0)
128    }
129}
130
131impl Debug for PersonId {
132    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
133        write!(f, "Person {}", self.0)
134    }
135}
136
137/// A trait that contains the initialization values for a
138/// new person. Do not use this directly, but instead use
139/// the tuple syntax.
140pub trait InitializationList {
141    fn has_property(&self, t: TypeId) -> bool;
142    fn set_properties(&self, context: &mut Context, person_id: PersonId);
143}
144
145// Implement the query version with 0 and 1 parameters
146impl InitializationList for () {
147    fn has_property(&self, _: TypeId) -> bool {
148        false
149    }
150    fn set_properties(&self, _context: &mut Context, _person_id: PersonId) {}
151}
152
153impl<T1: PersonProperty> InitializationList for (T1, T1::Value) {
154    fn has_property(&self, t: TypeId) -> bool {
155        t == TypeId::of::<T1>()
156    }
157
158    fn set_properties(&self, context: &mut Context, person_id: PersonId) {
159        context.set_person_property(person_id, T1::get_instance(), self.1);
160    }
161}
162
163// Implement the versions with 1..20 parameters.
164macro_rules! impl_initialization_list {
165    ($ct:expr) => {
166        seq!(N in 0..$ct {
167            impl<
168                #(
169                    T~N : PersonProperty,
170                )*
171            > InitializationList for (
172                #(
173                    (T~N, T~N::Value),
174                )*
175            )
176            {
177                fn has_property(&self, t: TypeId) -> bool {
178                    #(
179                        if t == TypeId::of::<T~N>() { return true; }
180                    )*
181                    return false
182                }
183
184                fn set_properties(&self, context: &mut Context, person_id: PersonId)  {
185                    #(
186                       context.set_person_property(person_id, T~N::get_instance(), self.N.1 );
187                    )*
188                }
189            }
190        });
191    }
192}
193
194seq!(Z in 1..20 {
195    impl_initialization_list!(Z);
196});