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::{Any, TypeId};
12use std::fmt::Debug;
13use std::hash::Hash;
14
15use crate::entity::property_store::get_property_dependents_static;
16use crate::entity::{Entity, EntityId};
17use crate::{Context, HashSet};
18
19/// The kind of initialization that a property has.
20#[derive(Copy, Clone, Eq, PartialEq, Debug)]
21pub enum PropertyInitializationKind {
22    /// The property is not derived and has no initial value. Its initialization is _explicit_, meaning it must be set
23    /// by client code at time of creation. Initialization is _explicit_ if and only if the property is _required_,
24    /// that is, if a value for the property must be supplied at time of entity creation.
25    Explicit,
26
27    /// The property is a derived property (it's value is computed dynamically from other property values). It cannot
28    /// be set explicitly.
29    Derived,
30
31    /// The property is given a constant initial value. Its initialization does not
32    /// trigger a change event.
33    Constant,
34}
35
36/// Shared trait bounds for property values and canonical values.
37///
38/// These values must be copyable and support equality and deterministic hashing so they can
39/// participate in indexing.
40pub trait AnyProperty: Copy + Debug + PartialEq + Eq + Hash + 'static {}
41impl<T> AnyProperty for T where T: Copy + Debug + PartialEq + Eq + Hash + 'static {}
42
43/// `const fn` string equality — `==` on `&str` isn't `const` on stable.
44#[must_use]
45pub const fn const_str_eq(a: &str, b: &str) -> bool {
46    if a.len() != b.len() {
47        return false;
48    }
49    let a = a.as_bytes();
50    let b = b.as_bytes();
51    let mut i = 0;
52    while i < a.len() {
53        if a[i] != b[i] {
54            return false;
55        }
56        i += 1;
57    }
58    true
59}
60
61/// All properties must implement this trait using one of the `define_property` macros.
62///
63/// Property values and canonical values must satisfy `AnyProperty` so they can participate in
64/// property indexes.
65pub trait Property<E: Entity>: AnyProperty {
66    /// Some properties might store a transformed version of the value in the index. This is the
67    /// type of the transformed value. For simple properties this will be the same as `Self`.
68    type CanonicalValue: AnyProperty;
69
70    /// Allocation-free representation of the query parts contributed by a property value.
71    type QueryParts<'a>: AsRef<[&'a dyn Any]>
72    where
73        Self: 'a;
74
75    /// Source-level name, set by the macros to `stringify!($property)`. Used by
76    /// `define_multi_property!` to reject type aliases (see issue #843).
77    const NAME: &'static str;
78
79    fn name() -> &'static str {
80        Self::NAME
81    }
82
83    /// The kind of initialization this property has.
84    #[must_use]
85    fn initialization_kind() -> PropertyInitializationKind;
86
87    #[must_use]
88    #[inline]
89    fn is_derived() -> bool {
90        Self::initialization_kind() == PropertyInitializationKind::Derived
91    }
92
93    #[must_use]
94    #[inline]
95    fn is_required() -> bool {
96        Self::initialization_kind() == PropertyInitializationKind::Explicit
97    }
98
99    /// Compute the value of the property, possibly by accessing the context and using the entity's ID.
100    #[must_use]
101    fn compute_derived(context: &Context, entity_id: EntityId<E>) -> Self;
102
103    /// Return the default initial constant value.
104    #[must_use]
105    fn default_const() -> Self;
106
107    /// This transforms a `Self` into a `Self::CanonicalValue`, e.g., for storage in an index.
108    /// For simple properties, this is the identity function.
109    #[must_use]
110    fn make_canonical(self) -> Self::CanonicalValue;
111
112    /// The inverse transform of `make_canonical`. For simple properties, this is the identity function.
113    #[must_use]
114    fn make_uncanonical(value: Self::CanonicalValue) -> Self;
115
116    /// Returns a string representation of the property value, e.g. for writing to a CSV file.
117    #[must_use]
118    fn get_display(&self) -> String;
119
120    /// Reconstruct the canonical query value used for indexed lookup.
121    ///
122    /// Ordinary properties expect a single query part containing `Self` and canonicalize that
123    /// value. Multi-properties override this to rebuild their canonical tuple value directly from
124    /// the already-sorted type-erased query parts.
125    #[must_use]
126    fn canonical_from_sorted_query_parts(parts: &[&dyn Any]) -> Option<Self::CanonicalValue> {
127        let [part] = parts else {
128            return None;
129        };
130        part.downcast_ref::<Self>()
131            .copied()
132            .map(Self::make_canonical)
133    }
134
135    /// Expose the query parts for a concrete property value without allocating.
136    ///
137    /// Ordinary properties contribute a single value. Multi-properties override this so singleton
138    /// queries over a multi-property can still be matched against a shared equivalent index.
139    #[must_use]
140    fn query_parts_for_value(value: &Self) -> Self::QueryParts<'_>;
141
142    /// Overridden by multi-properties, which use the `TypeId` of the ordered tuple so that tuples
143    /// with the same component types in a different order will have the same type ID.
144    #[must_use]
145    fn type_id() -> TypeId {
146        TypeId::of::<Self>()
147    }
148
149    /// For implementing the registry pattern
150    fn id() -> usize;
151
152    /// For properties that use the index of some other property, e.g. multi-properties, this
153    /// method gives the ID of the property index to use.
154    ///
155    /// Note that this is independent of whether or not the property actually is being indexed,
156    /// which is a property of the `Context` instance, not of the `Property<E>` type itself.
157    fn index_id() -> usize {
158        Self::id()
159    }
160
161    /// Returns a vector of transitive non-derived dependencies. If the property is not derived, the
162    /// Vec will be empty. The dependencies are represented by their `Property<E>::index()` value.
163    ///
164    /// This function is only used to construct the static dependency graph
165    /// within property `ctor`s, after which time the dependents of a property
166    /// are accessible through `Property<E>::dependents()` as a `&'static [usize]`.
167    fn non_derived_dependencies() -> Vec<usize> {
168        let mut result = HashSet::default();
169        Self::collect_non_derived_dependencies(&mut result);
170        result.into_iter().collect()
171    }
172
173    /// An auxiliary helper for `non_derived_dependencies` above.
174    fn collect_non_derived_dependencies(result: &mut HashSet<usize>);
175
176    /// Get a list of derived properties that depend on this property. The properties are
177    /// represented by their `Property::index()`. The list is pre-computed in `ctor`s.
178    fn dependents() -> &'static [usize] {
179        get_property_dependents_static::<E>(Self::id())
180    }
181}