dae_parser/api/
local_map.rs

1//! Contains the [`LocalMap`] and [`LocalMaps`] types, used to resolve internal ID references.
2
3use super::*;
4
5impl Document {
6    /// Run a function over all elements of type `T` in the document.
7    pub fn try_for_each<'a, T: Traversable + ?Sized + 'a, E, F: FnMut(&'a T) -> Result<(), E>>(
8        &'a self,
9        f: F,
10    ) -> Result<(), E> {
11        T::traverse(self, f)
12    }
13
14    /// Run a function over all elements of type `T` in the document.
15    pub fn for_each<'a, T: Traversable + ?Sized + 'a>(&'a self, mut f: impl FnMut(&'a T)) {
16        #[allow(clippy::unit_arg)]
17        self.try_for_each(|e| Ok(f(e)))
18            .unwrap_or_else(|e: std::convert::Infallible| match e {})
19    }
20
21    /// Construct an ID -> element mapping for node type `T`.
22    /// This can be used to look up ID references.
23    pub fn local_map<T: Traversable + HasId + ?Sized>(&self) -> Result<LocalMap<'_, T>> {
24        let mut map = LocalMap::default();
25        self.try_for_each(|v: &T| map.push(v))?;
26        Ok(map)
27    }
28
29    /// Convenience function, to return the main [`VisualScene`]
30    /// referred to in the `scene` field.
31    pub fn get_visual_scene(&self) -> Option<&VisualScene> {
32        let scene = self.scene.as_ref()?.instance_visual_scene.as_ref()?;
33        self.local_map().ok()?.get(&scene.url)
34    }
35
36    /// Construct an ID -> element mapping for every node type `T`.
37    /// This can be used to look up ID references.
38    ///
39    /// This function will initialize *every* type.
40    /// See [`LocalMaps::default`] and [`LocalMaps::set`] for a builder API
41    /// which allows you to pick which types you are interested in,
42    /// or [`LocalMaps::new`] and [`LocalMaps::unset`] to exclude certain types.
43    pub fn local_maps(&self) -> LocalMaps<'_> {
44        LocalMaps::new().collect(self)
45    }
46}
47
48/// A generic ID getter function.
49pub trait HasId {
50    /// Get the ID of the node.
51    fn id(&self) -> Option<&str>;
52
53    /// Extract the relevant `LocalMap` field from a `LocalMaps`.
54    fn get_local_map<'a, 'b>(maps: &'b LocalMaps<'a>) -> &'b Option<LocalMap<'a, Self>>;
55
56    /// Extract the relevant `LocalMap` field from a `LocalMaps`.
57    fn get_local_map_mut<'a, 'b>(maps: &'b mut LocalMaps<'a>)
58        -> &'b mut Option<LocalMap<'a, Self>>;
59}
60
61/// A map for looking up elements of type `T` in the document by ID.
62#[derive(Clone, Debug)]
63pub struct LocalMap<'a, T: ?Sized>(pub HashMap<&'a str, &'a T>);
64
65impl<'a, T: ?Sized> Default for LocalMap<'a, T> {
66    fn default() -> Self {
67        Self(Default::default())
68    }
69}
70
71impl<'a, T: ?Sized> LocalMap<'a, T> {
72    /// Look up an element by ID.
73    pub fn push(&mut self, v: &'a T) -> Result<()>
74    where
75        T: HasId,
76    {
77        if let Some(id) = v.id() {
78            if self.0.insert(id, v).is_some() {
79                return Err(format!("duplicate id {}", id).into());
80            }
81        }
82        Ok(())
83    }
84
85    /// Look up an element by ID.
86    pub fn get_str(&self, n: &str) -> Option<&'a T> {
87        self.0.get(n).copied()
88    }
89
90    /// Look up an element by ID, in a `NameRef`.
91    pub fn get_name(&self, n: &NameRef<T>) -> Option<&'a T> {
92        self.get_str(&n.val)
93    }
94
95    /// Look up an element by URL reference.
96    ///
97    /// This is a local map, meaning that it does not support references to URLs which are not
98    /// of the special form `#ref`, referring to an element with ID `ref` in the same document.
99    pub fn get_raw(&self, url: &Url) -> Option<&'a T> {
100        match url {
101            Url::Fragment(n) => self.get_str(n),
102            Url::Other(_) => None,
103        }
104    }
105
106    /// Look up an element by URL reference.
107    ///
108    /// This is a simple wrapper around [`get_raw`](Self::get_raw),
109    /// but it has better type safety, since it ensures that the reference is a reference to a `T`.
110    ///
111    /// This is a local map, meaning that it does not support references to URLs which are not
112    /// of the special form `#ref`, referring to an element with ID `ref` in the same document.
113    pub fn get(&self, url: &UrlRef<T>) -> Option<&'a T> {
114        self.get_raw(&url.val)
115    }
116}
117
118impl<'a, T: ?Sized> Index<&NameRef<T>> for LocalMap<'a, T> {
119    type Output = T;
120
121    fn index(&self, index: &NameRef<T>) -> &Self::Output {
122        self.get_name(index).unwrap()
123    }
124}
125
126impl<'a, T: ?Sized> Index<&UrlRef<T>> for LocalMap<'a, T> {
127    type Output = T;
128
129    fn index(&self, index: &UrlRef<T>) -> &Self::Output {
130        self.get(index).unwrap()
131    }
132}
133
134/// A trait for types that can be enumerated in a [`Document`]. This is used to power the
135/// [`for_each`](Document::for_each) and [`try_for_each`](Document::try_for_each) functions.
136pub trait Traversable {
137    /// Run the function `f` on all elements of type `Self` in the document `doc`.
138    fn traverse<'a, E>(
139        doc: &'a Document,
140        f: impl FnMut(&'a Self) -> Result<(), E>,
141    ) -> Result<(), E>
142    where
143        Self: 'a;
144}
145
146trait IdField {
147    fn try_to_str(&self) -> Option<&str>;
148}
149impl IdField for Option<String> {
150    fn try_to_str(&self) -> Option<&str> {
151        self.as_deref()
152    }
153}
154impl IdField for String {
155    fn try_to_str(&self) -> Option<&str> {
156        Some(self)
157    }
158}
159
160macro_rules! mk_local_maps {
161    ($($name:ident: $ty:ty,)*) => {
162        /// A data structure which maintains `LocalMap`s for every applicable type.
163        /// If you need to use [`Document::local_map`] for many different types,
164        /// it may be more efficient to use this type, which calculates all maps
165        /// in a single pass.
166        #[derive(Clone, Debug, Default)]
167        pub struct LocalMaps<'a> {
168            $($name: Option<LocalMap<'a, $ty>>,)*
169        }
170
171        impl LocalMaps<'_> {
172            /// Constructs a new `LocalMaps` with initializes all its fields to set but empty.
173            /// This differs from `default`, which initializes them to `None`.
174            /// Any `None` field will not be populated.
175            pub fn new() -> Self {
176                Self {
177                    $($name: Some(LocalMap::default()),)*
178                }
179            }
180        }
181        $(
182            impl HasId for $ty {
183                fn id(&self) -> Option<&str> {
184                    self.id.try_to_str()
185                }
186
187                fn get_local_map<'a, 'b>(maps: &'b LocalMaps<'a>) -> &'b Option<LocalMap<'a, Self>> {
188                    &maps.$name
189                }
190
191                fn get_local_map_mut<'a, 'b>(
192                    maps: &'b mut LocalMaps<'a>,
193                ) -> &'b mut Option<LocalMap<'a, Self>> {
194                    &mut maps.$name
195                }
196            }
197        )*
198    }
199}
200
201mk_local_maps! {
202    animation: Animation,
203    animation_clip: AnimationClip,
204    camera: Camera,
205    controller: Controller,
206    effect: Effect,
207    force_field: ForceField,
208    geometry: Geometry,
209    image: Image,
210    light: Light,
211    material: Material,
212    node: Node,
213    physics_material: PhysicsMaterial,
214    physics_model: PhysicsModel,
215    physics_scene: PhysicsScene,
216    sampler: Sampler,
217    source: Source,
218    vertices: Vertices,
219    visual_scene: VisualScene,
220    idref_array: IdRefArray,
221    name_array: NameArray,
222    bool_array: BoolArray,
223    float_array: FloatArray,
224    int_array: IntArray,
225}
226
227impl<'a> LocalMaps<'a> {
228    pub(crate) fn insert<T: HasId>(&mut self, t: &'a T) {
229        if let Some(id) = t.id() {
230            if let Some(map) = T::get_local_map_mut(self) {
231                assert!(map.0.insert(id, t).is_none());
232            }
233        }
234    }
235
236    /// Enable collection for the given type. This is used as part of a builder-style API for
237    /// customizing collection before performing the collection pass.
238    /// For example, if we only want to collect `Geometry` and `Source` types:
239    /// ```
240    /// # use dae_parser::*;
241    /// # fn foo(doc: Document) {
242    /// let maps = LocalMaps::default()
243    ///     .set::<Geometry>()
244    ///     .set::<Source>()
245    ///     .collect(&doc);
246    /// # }
247    /// ```
248    pub fn set<T: HasId + 'a>(mut self) -> Self {
249        T::get_local_map_mut(&mut self).get_or_insert_with(Default::default);
250        self
251    }
252
253    /// Disable collection for the given type. This is used as part of a builder-style API for
254    /// customizing collection before performing the collection pass.
255    /// For example, if we want to collect everything except the `ForceField` and `Sampler` types:
256    /// ```
257    /// # use dae_parser::*;
258    /// # fn foo(doc: Document) {
259    /// let maps = LocalMaps::new()
260    ///     .unset::<ForceField>()
261    ///     .unset::<Sampler>()
262    ///     .collect(&doc);
263    /// # }
264    /// ```
265    pub fn unset<T: HasId + 'a>(mut self) -> Self {
266        *T::get_local_map_mut(&mut self) = None;
267        self
268    }
269
270    /// Run the collection pass, putting all elements in the given IDs based on their types.
271    pub fn collect(mut self, t: &'a Document) -> Self {
272        t.collect_local_maps(&mut self);
273        self
274    }
275
276    /// Retrieve a map by type.
277    pub fn get_map<T: HasId + ?Sized>(&self) -> Option<&LocalMap<'a, T>> {
278        T::get_local_map(self).as_ref()
279    }
280
281    /// Look up an element by ID.
282    pub fn get_str<T: HasId + ?Sized>(&self, n: &str) -> Option<&'a T> {
283        self.get_map()?.get_str(n)
284    }
285
286    /// Look up an element by ID, in a `NameRef`.
287    pub fn get_name<T: HasId + ?Sized>(&self, n: &NameRef<T>) -> Option<&'a T> {
288        self.get_map()?.get_name(n)
289    }
290
291    /// Look up an element by URL reference.
292    ///
293    /// This is a local map, meaning that it does not support references to URLs which are not
294    /// of the special form `#ref`, referring to an element with ID `ref` in the same document.
295    pub fn get_raw<T: HasId + ?Sized>(&self, url: &Url) -> Option<&'a T> {
296        self.get_map()?.get_raw(url)
297    }
298
299    /// Look up an element by URL reference.
300    ///
301    /// This is a simple wrapper around [`get_raw`](Self::get_raw),
302    /// but it has better type safety, since it ensures that the reference is a reference to a `T`.
303    ///
304    /// This is a local map, meaning that it does not support references to URLs which are not
305    /// of the special form `#ref`, referring to an element with ID `ref` in the same document.
306    pub fn get<T: HasId + ?Sized>(&self, url: &UrlRef<T>) -> Option<&'a T> {
307        self.get_map()?.get(url)
308    }
309}
310
311impl<'a, T: HasId + ?Sized> Index<&NameRef<T>> for LocalMaps<'a> {
312    type Output = T;
313
314    fn index(&self, index: &NameRef<T>) -> &Self::Output {
315        self.get_name(index).unwrap()
316    }
317}
318
319impl<'a, T: HasId + ?Sized> Index<&UrlRef<T>> for LocalMaps<'a> {
320    type Output = T;
321
322    fn index(&self, index: &UrlRef<T>) -> &Self::Output {
323        self.get(index).unwrap()
324    }
325}
326
327pub(crate) trait CollectLocalMaps {
328    /// Insert this node and all its ID-containing children into the `maps` data structure.
329    fn collect_local_maps<'a>(&'a self, _: &mut LocalMaps<'a>) {}
330}
331
332impl<T: CollectLocalMaps> CollectLocalMaps for Option<T> {
333    fn collect_local_maps<'a>(&'a self, maps: &mut LocalMaps<'a>) {
334        if let Some(x) = self {
335            x.collect_local_maps(maps)
336        }
337    }
338}
339
340impl<T: CollectLocalMaps> CollectLocalMaps for Box<T> {
341    fn collect_local_maps<'a>(&'a self, maps: &mut LocalMaps<'a>) {
342        (**self).collect_local_maps(maps)
343    }
344}
345
346impl<T: CollectLocalMaps> CollectLocalMaps for Vec<T> {
347    fn collect_local_maps<'a>(&'a self, maps: &mut LocalMaps<'a>) {
348        for x in self {
349            x.collect_local_maps(maps)
350        }
351    }
352}