ixa/entity/
property_list.rs

1/*!
2
3A [`PropertyList<E>`] is just a tuple of distinct properties of the same [`Entity`] `E`. It
4is used in two distinct places: as an initialization list for a new entity, and as a query.
5
6Both use cases have the following two constraints:
7
81. The properties are properties of the same entity.
92. The properties are distinct.
10
11We enforce the first constraint with the type system by only implementing `PropertyList<E>`
12for tuples of types implementing `Property<E>` (of length up to some max). Using properties
13for mismatched entities will result in a nice compile-time error at the point of use.
14
15Unfortunately, the second constraint has to be enforced at runtime. We implement `PropertyList::validate()` to do this.
16
17For both use cases, the order in which the properties appear is
18unimportant in spite of the Rust language semantics of tuple types.
19
20*/
21
22use std::any::TypeId;
23
24use seq_macro::seq;
25
26use super::entity::{Entity, EntityId};
27use super::property::Property;
28use super::property_store::PropertyStore;
29use crate::entity::ContextEntitiesExt;
30use crate::{Context, IxaError};
31
32pub trait PropertyList<E: Entity>: Copy + 'static {
33    /// Validates that the properties are distinct. If not, returns an error describing the problematic properties.
34    fn validate() -> Result<(), IxaError>;
35
36    /// Checks that this property list includes all properties in the given list.
37    fn contains_properties(property_type_ids: &[TypeId]) -> bool;
38
39    /// Checks that this property list contains all required properties of the entity.
40    fn contains_required_properties() -> bool {
41        Self::contains_properties(E::required_property_ids())
42    }
43
44    /// Assigns the given entity the property values in `self` in the `property_store`.
45    /// This method does NOT emit property change events, as it is called upon entity creation.
46    fn set_values_for_new_entity(
47        &self,
48        entity_id: EntityId<E>,
49        property_store: &mut PropertyStore<E>,
50    );
51
52    /// Gets the tuple of property values for the given entity.
53    fn get_values_for_entity(context: &Context, entity_id: EntityId<E>) -> Self;
54}
55
56// The empty tuple is an empty `PropertyList<E>` for every `E: Entity`.
57impl<E: Entity> PropertyList<E> for () {
58    fn validate() -> Result<(), IxaError> {
59        Ok(())
60    }
61    fn contains_properties(property_type_ids: &[TypeId]) -> bool {
62        property_type_ids.is_empty()
63    }
64    fn set_values_for_new_entity(
65        &self,
66        _entity_id: EntityId<E>,
67        _property_store: &mut PropertyStore<E>,
68    ) {
69        // No values to assign.
70    }
71
72    fn get_values_for_entity(_context: &Context, _entity_id: EntityId<E>) -> Self {}
73}
74
75// An Entity ZST itself is an empty `PropertyList` for that entity.
76// This allows `context.add_entity(Person)` instead of `context.add_entity(())`.
77impl<E: Entity + Copy> PropertyList<E> for E {
78    fn validate() -> Result<(), IxaError> {
79        Ok(())
80    }
81    fn contains_properties(property_type_ids: &[TypeId]) -> bool {
82        property_type_ids.is_empty()
83    }
84    fn set_values_for_new_entity(
85        &self,
86        _entity_id: EntityId<E>,
87        _property_store: &mut PropertyStore<E>,
88    ) {
89        // No values to assign.
90    }
91
92    fn get_values_for_entity(_context: &Context, _entity_id: EntityId<E>) -> E {
93        E::default()
94    }
95}
96
97// ToDo(RobertJacobsonCDC): The following is a fundamental limitation in Rust. If downstream code *can* implement a
98//     trait impl that will cause conflicting implementations with some blanket impl, it disallows it, regardless of
99//     whether the conflict actually exists.
100// A single `Property` is a `PropertyList` of length 1
101// impl<E: Entity, P: Property<E>> PropertyList<E> for P {
102//     fn validate() -> Result<(), String> {
103//         Ok(())
104//     }
105//     fn contains_properties(property_type_ids: &[TypeId]) -> bool {
106//         property_type_ids.len() == 0
107//             || property_type_ids.len() == 1 && property_type_ids[0] == P::type_id()
108//     }
109//     fn set_values_for_new_entity(&self, entity_id: EntityId<E>, property_store: &mut PropertyStore<E>) {
110//         let property_value_store = property_store.get_mut::<P>();
111//         property_value_store.set(entity_id, *self);
112//     }
113// }
114
115// A single `Property` tuple is a `PropertyList` of length 1
116impl<E: Entity, P: Property<E>> PropertyList<E> for (P,) {
117    fn validate() -> Result<(), IxaError> {
118        Ok(())
119    }
120    fn contains_properties(property_type_ids: &[TypeId]) -> bool {
121        property_type_ids.is_empty()
122            || property_type_ids.len() == 1 && property_type_ids[0] == P::type_id()
123    }
124    fn set_values_for_new_entity(
125        &self,
126        entity_id: EntityId<E>,
127        property_store: &mut PropertyStore<E>,
128    ) {
129        let property_value_store = property_store.get_mut::<P>();
130        property_value_store.set(entity_id, self.0);
131    }
132
133    fn get_values_for_entity(context: &Context, entity_id: EntityId<E>) -> Self {
134        (context.get_property::<E, P>(entity_id),)
135    }
136}
137
138// Used only within this module.
139macro_rules! impl_property_list {
140    ($ct:literal) => {
141        seq!(N in 0..$ct {
142            impl<E: Entity, #( P~N: Property<E>,)*> PropertyList<E> for (#(P~N, )*){
143                fn validate() -> Result<(), IxaError> {
144                    // For `Property` distinctness check
145                    let property_type_ids: [TypeId; $ct] = [#(P~N::type_id(),)*];
146
147                    for i in 0..$ct - 1 {
148                        for j in (i + 1)..$ct {
149                            if property_type_ids[i] == property_type_ids[j] {
150                                return Err(IxaError::DuplicatePropertyInPropertyList {
151                                    first_index: i,
152                                    second_index: j,
153                                });
154                            }
155                        }
156                    }
157
158                    Ok(())
159                }
160
161                fn contains_properties(property_type_ids: &[TypeId]) -> bool {
162                    let self_property_type_ids: [TypeId; $ct] = [#(P~N::type_id(),)*];
163
164                    property_type_ids.len() <= $ct && property_type_ids.iter().all(|id| self_property_type_ids.contains(id))
165                }
166
167                fn set_values_for_new_entity(&self, entity_id: EntityId<E>, property_store: &mut PropertyStore<E>){
168                    #({
169                        let property_value_store = property_store.get_mut::<P~N>();
170                        property_value_store.set(entity_id, self.N);
171                    })*
172                }
173
174                fn get_values_for_entity(context: &Context, entity_id: EntityId<E>) -> Self {
175                    (#(context.get_property::<E, P~N>(entity_id), )*)
176                }
177            }
178        });
179    };
180}
181
182// Generate impls for tuple lengths 2 through 20. (The 0 and 1 case are implemented above.)
183seq!(Z in 2..=20 {
184    impl_property_list!(Z);
185});