moonshine_object/
hierarchy.rs

1use bevy_ecs::prelude::*;
2use bevy_ecs::query::{QueryData, QueryFilter, QueryItem};
3use moonshine_kind::{prelude::*, Any};
4
5use crate::{Object, ObjectInstance, ObjectName, ObjectRebind, ObjectRef, Objects};
6
7pub trait ObjectHierarchy<T: Kind = Any>: ObjectRebind<T> + ObjectName {
8    fn parent(&self) -> Option<Self::Rebind<Any>>;
9
10    fn root(&self) -> Self::Rebind<Any> {
11        self.ancestors()
12            .last()
13            .unwrap_or_else(|| self.rebind_any(self.entity()))
14    }
15
16    fn is_root(&self) -> bool {
17        self.parent().is_none()
18    }
19
20    fn is_child(&self) -> bool {
21        self.parent().is_some()
22    }
23
24    fn is_child_of(&self, parent: Entity) -> bool {
25        self.parent()
26            .is_some_and(|object| object.entity() == parent)
27    }
28
29    fn children(&self) -> impl Iterator<Item = Self::Rebind<Any>>;
30
31    fn has_children(&self) -> bool {
32        self.children().next().is_some()
33    }
34
35    fn query_children<'a, Q: QueryData, F: QueryFilter>(
36        &'a self,
37        query: &'a Query<'_, '_, Q, F>,
38    ) -> impl Iterator<Item = QueryItem<'a, Q::ReadOnly>> + 'a {
39        self.children()
40            .filter_map(move |object| query.get(object.entity()).ok())
41    }
42
43    fn children_of_kind<'a, U: Kind>(
44        &'a self,
45        objects: &'a Objects<'_, '_, U>,
46    ) -> impl Iterator<Item = Self::Rebind<U>> + 'a {
47        self.children()
48            .filter_map(move |object| objects.get(object.entity()).ok())
49            .map(|object| self.rebind_as(object.instance()))
50    }
51
52    fn find_child_of_kind<U: Kind>(&self, objects: &Objects<'_, '_, U>) -> Option<Self::Rebind<U>> {
53        self.children()
54            .find_map(|object| objects.get(object.entity()).ok())
55            .map(|object| self.rebind_as(object.instance()))
56    }
57
58    fn ancestors(&self) -> impl Iterator<Item = Self::Rebind<Any>>;
59
60    fn self_and_ancestors(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
61        std::iter::once(self.rebind_any(self.entity())).chain(self.ancestors())
62    }
63
64    fn is_ancestor_of(&self, entity: Entity) -> bool
65    where
66        Self::Rebind<Any>: ObjectHierarchy<Any>,
67    {
68        self.rebind_any(entity)
69            .ancestors()
70            .any(|ancestor| ancestor.entity() == self.entity())
71    }
72
73    fn query_ancestors<'a, Q: QueryData, F: QueryFilter>(
74        &'a self,
75        query: &'a Query<'_, '_, Q, F>,
76    ) -> impl Iterator<Item = QueryItem<'a, Q::ReadOnly>> + 'a {
77        self.ancestors().filter_map(move |object| {
78            let entity = object.entity();
79            query.get(entity).ok()
80        })
81    }
82
83    fn query_self_and_ancestors<'a, Q: QueryData, F: QueryFilter>(
84        &'a self,
85        query: &'a Query<'_, '_, Q, F>,
86    ) -> impl Iterator<Item = QueryItem<'a, Q::ReadOnly>> + 'a {
87        self.self_and_ancestors().filter_map(move |object| {
88            let entity = object.entity();
89            query.get(entity).ok()
90        })
91    }
92
93    fn ancestors_of_kind<'a, U: Kind>(
94        &'a self,
95        objects: &'a Objects<'_, '_, U>,
96    ) -> impl Iterator<Item = Self::Rebind<U>> + 'a {
97        self.ancestors()
98            .filter_map(move |object| objects.get(object.entity()).ok())
99            .map(|object| self.rebind_as(object.instance()))
100    }
101
102    fn find_ancestor_of_kind<U: Kind>(
103        &self,
104        objects: &Objects<'_, '_, U>,
105    ) -> Option<Self::Rebind<U>> {
106        self.ancestors()
107            .find_map(|object| objects.get(object.entity()).ok())
108            .map(|object| self.rebind_as(object.instance()))
109    }
110
111    fn descendants_wide(&self) -> impl Iterator<Item = Self::Rebind<Any>>;
112
113    fn descendants_deep(&self) -> impl Iterator<Item = Self::Rebind<Any>>;
114
115    fn self_and_descendants_wide(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
116        std::iter::once(self.rebind_any(self.entity())).chain(self.descendants_wide())
117    }
118
119    fn self_and_descendants_deep(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
120        std::iter::once(self.rebind_any(self.entity())).chain(self.descendants_deep())
121    }
122
123    fn is_descendant_of(&self, entity: Entity) -> bool
124    where
125        Self::Rebind<Any>: ObjectHierarchy<Any>,
126    {
127        self.ancestors().any(|ancestor| ancestor.entity() == entity)
128    }
129
130    fn descendants_of_kind_wide<'a, U: Kind>(
131        &'a self,
132        objects: &'a Objects<'_, '_, U>,
133    ) -> impl Iterator<Item = Self::Rebind<U>> + 'a {
134        self.descendants_wide()
135            .filter_map(move |object| objects.get(object.entity()).ok())
136            .map(|object| self.rebind_as(object.instance()))
137    }
138
139    fn descendants_of_kind_deep<'a, U: Kind>(
140        &'a self,
141        objects: &'a Objects<'_, '_, U>,
142    ) -> impl Iterator<Item = Self::Rebind<U>> + 'a {
143        self.descendants_deep()
144            .filter_map(move |object| objects.get(object.entity()).ok())
145            .map(|object| self.rebind_as(object.instance()))
146    }
147
148    fn query_descendants_wide<'a, Q: QueryData>(
149        &'a self,
150        query: &'a Query<'_, '_, Q>,
151    ) -> impl Iterator<Item = QueryItem<'a, Q::ReadOnly>> + 'a {
152        self.descendants_wide().filter_map(move |object| {
153            let entity = object.entity();
154            query.get(entity).ok()
155        })
156    }
157
158    fn query_descendants_deep<'a, Q: QueryData>(
159        &'a self,
160        query: &'a Query<'_, '_, Q>,
161    ) -> impl Iterator<Item = QueryItem<'a, Q::ReadOnly>> + 'a {
162        self.descendants_deep().filter_map(move |object| {
163            let entity = object.entity();
164            query.get(entity).ok()
165        })
166    }
167
168    fn query_self_and_descendants_wide<'a, Q: QueryData>(
169        &'a self,
170        query: &'a Query<'_, '_, Q>,
171    ) -> impl Iterator<Item = QueryItem<'a, Q::ReadOnly>> + 'a {
172        self.self_and_descendants_wide().filter_map(move |object| {
173            let entity = object.entity();
174            query.get(entity).ok()
175        })
176    }
177
178    fn query_self_and_descendants_deep<'a, Q: QueryData>(
179        &'a self,
180        query: &'a Query<'_, '_, Q>,
181    ) -> impl Iterator<Item = QueryItem<'a, Q::ReadOnly>> + 'a {
182        self.self_and_descendants_deep().filter_map(move |object| {
183            let entity = object.entity();
184            query.get(entity).ok()
185        })
186    }
187
188    fn find_descendant_of_kind_wide<U: Kind>(
189        &self,
190        objects: &Objects<'_, '_, U>,
191    ) -> Option<Self::Rebind<U>> {
192        self.descendants_wide()
193            .find_map(|object| objects.get(object.entity()).ok())
194            .map(|object| self.rebind_as(object.instance()))
195    }
196
197    fn find_descendant_of_kind_deep<U: Kind>(
198        &self,
199        objects: &Objects<'_, '_, U>,
200    ) -> Option<Self::Rebind<U>> {
201        self.descendants_deep()
202            .find_map(|object| objects.get(object.entity()).ok())
203            .map(|object| self.rebind_as(object.instance()))
204    }
205
206    /// Attempts to find an object by its path, relative to this one.
207    ///
208    /// # Usage
209    ///
210    /// An **Object Path** is a string of object names separated by slashes which represents
211    /// the path to an object within a hierarchy.
212    ///
213    /// In additional to object names, the path may contain the following special characters:
214    ///   - `.` represents this object.
215    ///   - `..` represents the parent object.
216    ///   - `*` represents any child object.
217    ///
218    /// Note that this method of object search is relatively slow, and should be reserved for
219    /// when performance is not the top priority, such as during initialization or prototyping.
220    ///
221    /// Instead, prefer to use [`Component`] to tag your entities and [`Query`] them instead, if possible.
222    ///
223    /// # Safety
224    /// This method is somewhat experimental with plans for future expansion.
225    /// Please [report](https://github.com/Zeenobit/moonshine_object/issues) any bugs you encounter or features you'd like.
226    fn find_by_path(&self, path: impl AsRef<str>) -> Option<Self::Rebind<Any>>;
227}
228
229impl<'w, 's, 'a, T: Kind> ObjectHierarchy<T> for Object<'w, 's, 'a, T> {
230    fn parent(&self) -> Option<Self::Rebind<Any>> {
231        self.hierarchy
232            .parent(self.entity())
233            .map(|entity| self.rebind_any(entity))
234    }
235
236    fn children(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
237        self.hierarchy
238            .children(self.entity())
239            .map(|entity| self.rebind_any(entity))
240    }
241
242    fn ancestors(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
243        self.hierarchy
244            .ancestors(self.entity())
245            .map(|entity| self.rebind_any(entity))
246    }
247
248    fn descendants_wide(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
249        self.hierarchy
250            .descendants_wide(self.entity())
251            .map(|entity| self.rebind_any(entity))
252    }
253
254    fn descendants_deep(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
255        self.hierarchy
256            .descendants_deep(self.entity())
257            .map(|entity| self.rebind_any(entity))
258    }
259
260    fn find_by_path(&self, path: impl AsRef<str>) -> Option<Self::Rebind<Any>> {
261        let tail: Vec<&str> = path.as_ref().split('/').collect();
262        find_by_path(self.cast_into_any(), &tail)
263    }
264}
265
266impl<'w, 's, 'a, T: Kind> ObjectHierarchy<T> for ObjectRef<'w, 's, 'a, T> {
267    fn parent(&self) -> Option<Self::Rebind<Any>> {
268        self.1.parent().map(|object| ObjectRef(self.0, object))
269    }
270
271    fn children(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
272        self.1.children().map(|object| ObjectRef(self.0, object))
273    }
274
275    fn ancestors(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
276        self.1.ancestors().map(|object| ObjectRef(self.0, object))
277    }
278
279    fn descendants_wide(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
280        self.1
281            .descendants_wide()
282            .map(|object| ObjectRef(self.0, object))
283    }
284
285    fn descendants_deep(&self) -> impl Iterator<Item = Self::Rebind<Any>> {
286        self.1
287            .descendants_deep()
288            .map(|object| ObjectRef(self.0, object))
289    }
290
291    fn find_by_path(&self, path: impl AsRef<str>) -> Option<Self::Rebind<Any>> {
292        self.1
293            .find_by_path(path)
294            .map(|object| ObjectRef(self.0, object))
295    }
296}
297
298fn find_by_path<T: ObjectHierarchy<Rebind<Any> = T>>(
299    curr: T,
300    tail: &[&str],
301) -> Option<T::Rebind<Any>> {
302    if tail.is_empty() {
303        return Some(curr);
304    }
305
306    let head = tail[0];
307    let tail = &tail[1..];
308
309    if head == "." || head.is_empty() {
310        find_by_path(curr, tail)
311    } else if head == ".." {
312        if let Some(parent) = curr.parent() {
313            find_by_path(parent, tail)
314        } else {
315            None
316        }
317    } else if head == "*" {
318        for child in curr.children() {
319            if let Some(result) = find_by_path(child, tail) {
320                return Some(result);
321            }
322        }
323        return None;
324    } else if let Some(child) = curr
325        .children()
326        .find(|part| part.name().is_some_and(|name| name == head))
327    {
328        find_by_path(child, tail)
329    } else {
330        None
331    }
332}