ixa/entity/
property.rs

1/*!
2
3A `Property` is the value type for properties associated to an `Entity`.
4
5The `Property` trait should be implemented only with one of the macros `define_property!`, `impl_property!`,
6`define_derived_property!`, `impl_derived_property!`, or `define_multi_property!` to ensure correct and consistent
7implementation.
8
9*/
10
11use std::any::TypeId;
12use std::fmt::Debug;
13
14use serde::Serialize;
15
16use crate::entity::property_store::get_property_dependents_static;
17use crate::entity::{Entity, EntityId};
18use crate::hashing::hash_serialized_128;
19use crate::{Context, HashSet};
20
21/// The kind of initialization that a property has.
22#[derive(Copy, Clone, Eq, PartialEq, Debug)]
23pub enum PropertyInitializationKind {
24    /// The property is not derived and has no initial value. Its initialization is _explicit_, meaning it must be set
25    /// by client code at time of creation. Initialization is _explicit_ if and only if the property is _required_,
26    /// that is, if a value for the property must be supplied at time of entity creation.
27    Explicit,
28
29    /// The property is a derived property (it's value is computed dynamically from other property values). It cannot
30    /// be set explicitly.
31    Derived,
32
33    /// The property is given a constant initial value. Its initialization does not
34    /// trigger a change event.
35    Constant,
36}
37
38// A type-erased interface for properties.
39pub trait AnyProperty: Copy + Debug + PartialEq + Serialize + 'static {}
40impl<T> AnyProperty for T where T: Copy + Debug + PartialEq + Serialize + 'static {}
41
42/// All properties must implement this trait using one of the `define_property` macros.
43pub trait Property<E: Entity>: AnyProperty {
44    /// Some properties might store a transformed version of the value in the index. This is the
45    /// type of the transformed value. For simple properties this will be the same as `Self`.
46    type CanonicalValue: AnyProperty;
47
48    fn name() -> &'static str {
49        let full = std::any::type_name::<Self>();
50        full.rsplit("::").next().unwrap()
51    }
52
53    /// The kind of initialization this property has.
54    #[must_use]
55    fn initialization_kind() -> PropertyInitializationKind;
56
57    #[must_use]
58    #[inline]
59    fn is_derived() -> bool {
60        Self::initialization_kind() == PropertyInitializationKind::Derived
61    }
62
63    #[must_use]
64    #[inline]
65    fn is_required() -> bool {
66        Self::initialization_kind() == PropertyInitializationKind::Explicit
67    }
68
69    /// Compute the value of the property, possibly by accessing the context and using the entity's ID.
70    #[must_use]
71    fn compute_derived(context: &Context, entity_id: EntityId<E>) -> Self;
72
73    /// Return the default initial constant value.
74    #[must_use]
75    fn default_const() -> Self;
76
77    /// This transforms a `Self` into a `Self::CanonicalValue`, e.g., for storage in an index.
78    /// For simple properties, this is the identity function.
79    #[must_use]
80    fn make_canonical(self) -> Self::CanonicalValue;
81
82    /// The inverse transform of `make_canonical`. For simple properties, this is the identity function.
83    #[must_use]
84    fn make_uncanonical(value: Self::CanonicalValue) -> Self;
85
86    /// Returns a string representation of the property value, e.g. for writing to a CSV file.
87    #[must_use]
88    fn get_display(&self) -> String;
89
90    /// For cases when the property's hash needs to be computed in a special way.
91    #[must_use]
92    fn hash_property_value(value: &Self::CanonicalValue) -> u128 {
93        hash_serialized_128(value)
94    }
95
96    /// Overridden by multi-properties, which use the `TypeId` of the ordered tuple so that tuples
97    /// with the same component types in a different order will have the same type ID.
98    #[must_use]
99    fn type_id() -> TypeId {
100        TypeId::of::<Self>()
101    }
102
103    /// For implementing the registry pattern
104    fn id() -> usize;
105
106    /// For properties that use the index of some other property, e.g. multi-properties, this
107    /// method gives the ID of the property index to use.
108    ///
109    /// Note that this is independent of whether or not the property actually is being indexed,
110    /// which is a property of the `Context` instance, not of the `Property<E>` type itself.
111    fn index_id() -> usize {
112        Self::id()
113    }
114
115    /// Returns a vector of transitive non-derived dependencies. If the property is not derived, the
116    /// Vec will be empty. The dependencies are represented by their `Property<E>::index()` value.
117    ///
118    /// This function is only used to construct the static dependency graph
119    /// within property `ctor`s, after which time the dependents of a property
120    /// are accessible through `Property<E>::dependents()` as a `&'static [usize]`.
121    fn non_derived_dependencies() -> Vec<usize> {
122        let mut result = HashSet::default();
123        Self::collect_non_derived_dependencies(&mut result);
124        result.into_iter().collect()
125    }
126
127    /// An auxiliary helper for `non_derived_dependencies` above.
128    fn collect_non_derived_dependencies(result: &mut HashSet<usize>);
129
130    /// Get a list of derived properties that depend on this property. The properties are
131    /// represented by their `Property::index()`. The list is pre-computed in `ctor`s.
132    fn dependents() -> &'static [usize] {
133        get_property_dependents_static::<E>(Self::id())
134    }
135}