moonshine_object/
hierarchy.rs

1use bevy_ecs::prelude::*;
2use bevy_ecs::query::{QueryData, QueryFilter, QueryItem};
3use moonshine_kind::{prelude::*, Any};
4use moonshine_util::hierarchy::{WorldDescendantsDeepIter, WorldDescendantsWideIter};
5
6use crate::{Object, ObjectName, ObjectRebind, ObjectRef, ObjectWorldRef, Objects};
7
8/// [`Object`] methods related to hierarchy traversal.
9///
10/// These methods are available to any [`Object<T>`] or [`ObjectRef<T>`] type.
11pub trait ObjectHierarchy<T: Kind = Any>: ObjectRebind<T> + ObjectName {
12    /// Returns the parent of this object, if it exists.
13    fn parent(&self) -> Option<Self::Rebind<Any>>;
14
15    /// Returns the root of this object's hierarchy.
16    fn root(&self) -> Self::Rebind<Any> {
17        self.ancestors()
18            .last()
19            // SAFE: If this object is valid, then so must be its root
20            .unwrap_or_else(|| unsafe { self.rebind_any(self.entity()) })
21    }
22
23    /// Returns true if this object is the root of its hierarchy.
24    fn is_root(&self) -> bool {
25        self.parent().is_none()
26    }
27
28    /// Returns true if this object has the same root as another.
29    fn is_related_to<U: Kind>(&self, other: &impl ObjectHierarchy<U>) -> bool {
30        self.root().entity() == other.root().entity()
31    }
32
33    /// Returns true if this object is a child of another.
34    fn is_child(&self) -> bool {
35        self.parent().is_some()
36    }
37
38    /// Returns true if this object is a child of the given [`Entity`].
39    fn is_child_of(&self, entity: Entity) -> bool {
40        self.parent()
41            .is_some_and(|object| object.entity() == entity)
42    }
43
44    /// Iterates over all children of this object.
45    fn children(&self) -> impl Iterator<Item = Self::Rebind<Any>>;
46
47    /// Returns true if this object has any children.
48    fn has_children(&self) -> bool {
49        self.children().next().is_some()
50    }
51
52    /// Iterates over all children of this object which match the given [`Query`].
53    fn query_children<'a, Q: QueryData, F: QueryFilter>(
54        &'a self,
55        query: &'a Query<Q, F>,
56    ) -> impl Iterator<Item = QueryItem<'a, 'a, Q::ReadOnly>> + 'a {
57        self.children()
58            .filter_map(move |object| query.get(object.entity()).ok())
59    }
60
61    /// Iterates over all children of this object which match the given [`Kind`].
62    fn children_of_kind<'w, 's, 'a, U: Kind>(
63        &'a self,
64        objects: &'a Objects<'w, 's, U>,
65    ) -> impl Iterator<Item = Object<'w, 's, 'a, U>> + 'a {
66        self.children()
67            .filter_map(move |object| objects.get(object.entity()).ok())
68    }
69
70    /// Returns the first child of this object which matches the given kind, if it exists.
71    fn find_child_of_kind<'w, 's, 'a, U: Kind>(
72        &self,
73        objects: &'a Objects<'w, 's, U>,
74    ) -> Option<Object<'w, 's, 'a, U>> {
75        self.children()
76            .find_map(|object| objects.get(object.entity()).ok())
77    }
78
79    /// Iterates over all ancestors of this object.
80    fn ancestors(&self) -> impl Iterator<Item = Self::Rebind<Any>>;
81
82    /// Iterates over this object, followed by all of its ancestors.
83    fn self_and_ancestors(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
84        std::iter::Iterator::chain(std::iter::once(self.as_any()), self.ancestors())
85    }
86
87    /// Returns true if this object is an ancestor of another.
88    fn is_ancestor_of<U: Kind>(&self, other: &impl ObjectHierarchy<U>) -> bool
89    where
90        Self::Rebind<Any>: ObjectHierarchy<Any>,
91    {
92        other
93            .ancestors()
94            .any(|ancestor| ancestor.entity() == self.entity())
95    }
96
97    /// Iterates over all ancestors of this object which match the given [`Query`].
98    fn query_ancestors<'a, Q: QueryData, F: QueryFilter>(
99        &'a self,
100        query: &'a Query<Q, F>,
101    ) -> impl Iterator<Item = QueryItem<'a, 'a, Q::ReadOnly>> + 'a {
102        self.ancestors().filter_map(move |object| {
103            let entity = object.entity();
104            query.get(entity).ok()
105        })
106    }
107
108    /// Iterates over this object, followed by all its ancestors which match the given [`Query`].
109    fn query_self_and_ancestors<'a, Q: QueryData, F: QueryFilter>(
110        &'a self,
111        query: &'a Query<Q, F>,
112    ) -> impl Iterator<Item = QueryItem<'a, 'a, Q::ReadOnly>> + 'a {
113        self.self_and_ancestors().filter_map(move |object| {
114            let entity = object.entity();
115            query.get(entity).ok()
116        })
117    }
118
119    /// Iterates over all ancestors of this object which match the given [`Kind`].
120    fn ancestors_of_kind<'w, 's, 'a, U: Kind>(
121        &'a self,
122        objects: &'a Objects<'w, 's, U>,
123    ) -> impl Iterator<Item = Object<'w, 's, 'a, U>> {
124        self.ancestors()
125            .filter_map(move |object| objects.get(object.entity()).ok())
126    }
127
128    /// Iterates over this object, followed by all its ancestors which match the given [`Kind`].
129    fn self_and_ancestors_of_kind<'w, 's, 'a, U: Kind>(
130        &'a self,
131        objects: &'a Objects<'w, 's, U>,
132    ) -> impl Iterator<Item = Object<'w, 's, 'a, U>> {
133        self.self_and_ancestors()
134            .filter_map(move |object| objects.get(object.entity()).ok())
135    }
136
137    /// Returns the first ancestor of this object which matches the given [`Kind`], if it exists.
138    fn find_ancestor_of_kind<'w, 's, 'a, U: Kind>(
139        &self,
140        objects: &'a Objects<'w, 's, U>,
141    ) -> Option<Object<'w, 's, 'a, U>> {
142        self.ancestors()
143            .find_map(|object| objects.get(object.entity()).ok())
144    }
145
146    /// Iterates over all descendants of this object in breadth-first order.
147    fn descendants_wide(&self) -> impl Iterator<Item = Self::Rebind<Any>>;
148
149    /// Iterates over all descendants of this object in depth-first order.
150    fn descendants_deep(&self) -> impl Iterator<Item = Self::Rebind<Any>>;
151
152    /// Iterates over this object and all its descendants in breadth-first order.
153    fn self_and_descendants_wide(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
154        std::iter::Iterator::chain(std::iter::once(self.as_any()), self.descendants_wide())
155    }
156
157    /// Iterates over this object and all its descendants in depth-first order.
158    fn self_and_descendants_deep(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
159        std::iter::Iterator::chain(std::iter::once(self.as_any()), self.descendants_deep())
160    }
161
162    /// Returns true if this object is a descendant of the given entity.
163    fn is_descendant_of(&self, entity: Entity) -> bool
164    where
165        Self::Rebind<Any>: ObjectHierarchy<Any>,
166    {
167        self.ancestors().any(|ancestor| ancestor.entity() == entity)
168    }
169
170    /// Iterates over all descendants of this object which match the given [`Kind`] in breadth-first order.
171    fn descendants_of_kind_wide<'w, 's, 'a, U: Kind>(
172        &'a self,
173        objects: &'a Objects<'w, 's, U>,
174    ) -> impl Iterator<Item = Object<'w, 's, 'a, U>> {
175        self.descendants_wide()
176            .filter_map(move |object| objects.get(object.entity()).ok())
177    }
178
179    /// Iterates over all descendants of this object which match the given [`Kind`] in depth-first order.
180    fn descendants_of_kind_deep<'w, 's, 'a, U: Kind>(
181        &'a self,
182        objects: &'a Objects<'w, 's, U>,
183    ) -> impl Iterator<Item = Object<'w, 's, 'a, U>> {
184        self.descendants_deep()
185            .filter_map(move |object| objects.get(object.entity()).ok())
186    }
187
188    /// Iterates over this object, followed by all its descendants which match the given [`Kind`] in breadth-first order.
189    fn self_and_descendants_of_kind_wide<'w, 's, 'a, U: Kind>(
190        &'a self,
191        objects: &'a Objects<'w, 's, U>,
192    ) -> impl Iterator<Item = Object<'w, 's, 'a, U>> {
193        self.self_and_descendants_wide()
194            .filter_map(move |object| objects.get(object.entity()).ok())
195    }
196
197    /// Iterates over this object, followed by all its descendants which match the given [`Kind`] in depth-first order.
198    fn self_and_descendants_of_kind_deep<'w, 's, 'a, U: Kind>(
199        &'a self,
200        objects: &'a Objects<'w, 's, U>,
201    ) -> impl Iterator<Item = Object<'w, 's, 'a, U>> {
202        self.self_and_descendants_deep()
203            .filter_map(move |object| objects.get(object.entity()).ok())
204    }
205
206    /// Iterates over all descendants of this object which match the given [`Query`] in breadth-first order.
207    fn query_descendants_wide<'a, Q: QueryData, F: QueryFilter>(
208        &'a self,
209        query: &'a Query<Q, F>,
210    ) -> impl Iterator<Item = QueryItem<'a, 'a, Q::ReadOnly>> + 'a {
211        self.descendants_wide().filter_map(move |object| {
212            let entity = object.entity();
213            query.get(entity).ok()
214        })
215    }
216
217    /// Iterates over all descendants of this object which match the given [`Kind`] in depth-first order.
218    fn query_descendants_deep<'a, Q: QueryData, F: QueryFilter>(
219        &'a self,
220        query: &'a Query<Q, F>,
221    ) -> impl Iterator<Item = QueryItem<'a, 'a, Q::ReadOnly>> + 'a {
222        self.descendants_deep().filter_map(move |object| {
223            let entity = object.entity();
224            query.get(entity).ok()
225        })
226    }
227
228    /// Iterates over this object, followed by all its descendants which match the given [`Query`] in breadth-first order.
229    fn query_self_and_descendants_wide<'a, Q: QueryData, F: QueryFilter>(
230        &'a self,
231        query: &'a Query<Q, F>,
232    ) -> impl Iterator<Item = QueryItem<'a, 'a, Q::ReadOnly>> + 'a {
233        self.self_and_descendants_wide().filter_map(move |object| {
234            let entity = object.entity();
235            query.get(entity).ok()
236        })
237    }
238
239    /// Iterates over this object, followed by all its descendants which match the given [`Query`] in depth-first order.
240    fn query_self_and_descendants_deep<'a, Q: QueryData>(
241        &'a self,
242        query: &'a Query<Q>,
243    ) -> impl Iterator<Item = QueryItem<'a, 'a, Q::ReadOnly>> + 'a {
244        self.self_and_descendants_deep().filter_map(move |object| {
245            let entity = object.entity();
246            query.get(entity).ok()
247        })
248    }
249
250    /// Returns the first descendant of this object (breadth-first order) which matches the given [`Kind`], if it exists.
251    fn find_descendant_of_kind_wide<'w, 's, 'a, U: Kind>(
252        &self,
253        objects: &'a Objects<'w, 's, U>,
254    ) -> Option<Object<'w, 's, 'a, U>> {
255        self.descendants_wide()
256            .find_map(|object| objects.get(object.entity()).ok())
257    }
258
259    /// Returns the first descendant of this object (depth-first order) which matches the given [`Kind`], if it exists.
260    fn find_descendant_of_kind_deep<'w, 's, 'a, U: Kind>(
261        &self,
262        objects: &'a Objects<'w, 's, U>,
263    ) -> Option<Object<'w, 's, 'a, U>> {
264        self.descendants_deep()
265            .find_map(|object| objects.get(object.entity()).ok())
266    }
267
268    /// Returns the path to this object.
269    fn path(&self) -> String {
270        // TODO: Can this be optimized?
271        let mut tokens = self
272            .self_and_ancestors()
273            .map(|ancestor| ancestor.name().unwrap_or_default().to_string())
274            .collect::<Vec<_>>();
275        tokens.reverse();
276        tokens.join("/")
277    }
278
279    /// Attempts to find an object by its path, relative to this one.
280    ///
281    /// # Usage
282    ///
283    /// An **Object Path** is a string of object names separated by slashes which represents
284    /// the path to an object within a hierarchy.
285    ///
286    /// In additional to object names, the path may contain the following special characters:
287    ///   - `.` represents this object.
288    ///   - `..` represents the parent object.
289    ///   - `*` represents any child object.
290    ///
291    /// Note that this method of object search is relatively slow, and should be reserved for
292    /// when performance is not the top priority, such as during initialization or prototyping.
293    ///
294    /// Instead, prefer to use [`Component`] to tag your entities and [`Query`] them instead, if possible.
295    ///
296    /// # Safety
297    /// This method is somewhat experimental with plans for future expansion.
298    /// Please [report](https://github.com/Zeenobit/moonshine_object/issues) any bugs you encounter or features you'd like.
299    fn find_by_path(&self, path: impl AsRef<str>) -> Option<Self::Rebind<Any>>;
300}
301
302impl<T: Kind> ObjectHierarchy<T> for Object<'_, '_, '_, T> {
303    fn parent(&self) -> Option<Self::Rebind<Any>> {
304        self.hierarchy
305            .parent(self.entity())
306            // SAFE: If this object is valid, then so must be its parent
307            .map(|entity| unsafe { self.rebind_any(entity) })
308    }
309
310    fn children(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
311        self.hierarchy
312            .children(self.entity())
313            // SAFE: We assume Bevy removes invalid children
314            .map(|entity| unsafe { self.rebind_any(entity) })
315    }
316
317    fn ancestors(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
318        self.hierarchy
319            .ancestors(self.entity())
320            // SAFE: If this object is valid, then so must be its ancestors
321            .map(|entity| unsafe { self.rebind_any(entity) })
322    }
323
324    fn descendants_wide(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
325        self.hierarchy
326            .descendants_wide(self.entity())
327            // SAFE: We assume Bevy removes invalid children
328            .map(|entity| unsafe { self.rebind_any(entity) })
329    }
330
331    fn descendants_deep(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
332        self.hierarchy
333            .descendants_deep(self.entity())
334            // SAFE: We assume Bevy removes invalid children
335            .map(|entity| unsafe { self.rebind_any(entity) })
336    }
337
338    fn find_by_path(&self, path: impl AsRef<str>) -> Option<Self::Rebind<Any>> {
339        let tail: Vec<&str> = path.as_ref().split('/').collect();
340        find_by_path(self.cast_into_any(), &tail)
341    }
342}
343
344impl<T: Kind> ObjectHierarchy<T> for ObjectRef<'_, '_, '_, T> {
345    fn parent(&self) -> Option<Self::Rebind<Any>> {
346        self.1.parent().map(|object| ObjectRef(self.0, object))
347    }
348
349    fn children(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
350        self.1.children().map(|object| ObjectRef(self.0, object))
351    }
352
353    fn ancestors(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
354        self.1.ancestors().map(|object| ObjectRef(self.0, object))
355    }
356
357    fn descendants_wide(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
358        self.1
359            .descendants_wide()
360            .map(|object| ObjectRef(self.0, object))
361    }
362
363    fn descendants_deep(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
364        self.1
365            .descendants_deep()
366            .map(|object| ObjectRef(self.0, object))
367    }
368
369    fn find_by_path(&self, path: impl AsRef<str>) -> Option<Self::Rebind<Any>> {
370        self.1
371            .find_by_path(path)
372            .map(|object| ObjectRef(self.0, object))
373    }
374}
375
376impl<T: Kind> ObjectHierarchy<T> for ObjectWorldRef<'_, T> {
377    fn parent(&self) -> Option<Self::Rebind<Any>> {
378        let &ChildOf(parent) = self.world.get(self.entity())?;
379        // SAFE: If this object is valid, then so must be its parent
380        Some(unsafe { self.rebind_any(parent) })
381    }
382
383    fn children(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
384        self.world
385            .get::<Children>(self.entity())
386            .into_iter()
387            .flat_map(|children| children.iter())
388            // SAFE: Assume Bevy removes invalid children
389            .map(|entity| unsafe { self.rebind_any(entity) })
390    }
391
392    fn ancestors(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
393        std::iter::successors(self.parent(), |current| current.parent())
394    }
395
396    fn descendants_wide(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
397        WorldDescendantsWideIter::new(self.world, self.entity())
398            // SAFE: Assume Bevy resomves invalid descendants
399            .map(|entity| unsafe { self.rebind_any(entity) })
400    }
401
402    fn descendants_deep(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
403        WorldDescendantsDeepIter::new(self.world, self.entity())
404            // SAFE: Assume Bevy resomves invalid descendants
405            .map(|entity| unsafe { self.rebind_any(entity) })
406    }
407
408    fn find_by_path(&self, path: impl AsRef<str>) -> Option<Self::Rebind<Any>> {
409        let tail: Vec<&str> = path.as_ref().split('/').collect();
410        find_by_path(self.cast_into_any(), &tail)
411    }
412}
413
414fn find_by_path<T: ObjectHierarchy<Rebind<Any> = T>>(
415    curr: T,
416    tail: &[&str],
417) -> Option<T::Rebind<Any>> {
418    if tail.is_empty() {
419        return Some(curr);
420    }
421
422    let head = tail[0];
423    let tail = &tail[1..];
424
425    if head == "." || head.is_empty() {
426        find_by_path(curr, tail)
427    } else if head == ".." {
428        if let Some(parent) = curr.parent() {
429            find_by_path(parent, tail)
430        } else {
431            None
432        }
433    } else if head == "*" {
434        for child in curr.children() {
435            if let Some(result) = find_by_path(child, tail) {
436                return Some(result);
437            }
438        }
439        None
440    } else if let Some(child) = curr
441        .children()
442        .find(|part| part.name().is_some_and(|name| name == head))
443    {
444        find_by_path(child, tail)
445    } else {
446        None
447    }
448}