fyrox_resource/
loader.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Resource loader. It manages resource loading.
22
23use crate::{
24    core::uuid::Uuid, io::ResourceIo, options::BaseImportOptions, state::LoadError, ResourceData,
25};
26use fyrox_core::Downcast;
27use std::{future::Future, path::PathBuf, pin::Pin, sync::Arc};
28
29#[cfg(target_arch = "wasm32")]
30#[doc(hidden)]
31pub trait BaseResourceLoader: Downcast {}
32
33#[cfg(not(target_arch = "wasm32"))]
34#[doc(hidden)]
35pub trait BaseResourceLoader: Send + Downcast {}
36
37impl<T> BaseResourceLoader for T where T: ResourceLoader {}
38
39/// Trait for resource loading.
40pub trait ResourceLoader: BaseResourceLoader {
41    /// Returns a list of file extensions supported by the loader. Resource manager will use this list
42    /// to pick the correct resource loader when the user requests a resource.
43    fn extensions(&self) -> &[&str];
44
45    /// Checks if the given extension is supported by this loader. Comparison is case-insensitive.
46    fn supports_extension(&self, ext: &str) -> bool {
47        self.extensions()
48            .iter()
49            .any(|e| fyrox_core::cmp_strings_case_insensitive(e, ext))
50    }
51
52    /// Must return a type uuid of the resource data type.
53    fn data_type_uuid(&self) -> Uuid;
54
55    /// Loads or reloads a resource.
56    fn load(&self, path: PathBuf, io: Arc<dyn ResourceIo>) -> BoxedLoaderFuture;
57
58    /// Tries to load import settings for a resource.
59    fn try_load_import_settings(
60        &self,
61        #[allow(unused_variables)] resource_path: PathBuf,
62        #[allow(unused_variables)] io: Arc<dyn ResourceIo>,
63    ) -> BoxedImportOptionsLoaderFuture {
64        Box::pin(async move { None })
65    }
66
67    /// Returns default import options for the resource.
68    fn default_import_options(&self) -> Option<Box<dyn BaseImportOptions>> {
69        None
70    }
71}
72
73pub struct LoaderPayload(pub(crate) Box<dyn ResourceData>);
74
75impl LoaderPayload {
76    pub fn new<T: ResourceData>(data: T) -> Self {
77        Self(Box::new(data))
78    }
79}
80
81/// Future type for resource loading. See 'ResourceLoader'.
82#[cfg(target_arch = "wasm32")]
83pub type BoxedLoaderFuture = Pin<Box<dyn Future<Output = Result<LoaderPayload, LoadError>>>>;
84
85/// Future type for resource loading. See 'ResourceLoader'.
86#[cfg(not(target_arch = "wasm32"))]
87pub type BoxedLoaderFuture = Pin<Box<dyn Future<Output = Result<LoaderPayload, LoadError>> + Send>>;
88
89/// Future type for resource import options loading.
90pub type BoxedImportOptionsLoaderFuture =
91    Pin<Box<dyn Future<Output = Option<Box<dyn BaseImportOptions>>>>>;
92
93/// Container for resource loaders.
94#[derive(Default)]
95pub struct ResourceLoadersContainer {
96    loaders: Vec<Box<dyn ResourceLoader>>,
97}
98
99impl ResourceLoadersContainer {
100    /// Creates new empty resource loaders container.
101    pub fn new() -> Self {
102        Self::default()
103    }
104
105    /// Adds new resource loader or replaces existing. There could be only one loader of a given type
106    /// at the same time. You can use this method to replace resource loaders with your own loaders.
107    pub fn set<T>(&mut self, loader: T) -> Option<T>
108    where
109        T: ResourceLoader,
110    {
111        if let Some(existing_loader) = self
112            .loaders
113            .iter_mut()
114            .find_map(|l| (**l).as_any_mut().downcast_mut::<T>())
115        {
116            Some(std::mem::replace(existing_loader, loader))
117        } else {
118            self.loaders.push(Box::new(loader));
119            None
120        }
121    }
122
123    /// Searches for an instance of a resource loader of type `Prev` and replaces it with an other instance
124    /// of a type `New`.
125    pub fn try_replace<Prev, New>(&mut self, new_loader: New) -> Option<Prev>
126    where
127        Prev: ResourceLoader,
128        New: ResourceLoader,
129    {
130        if let Some(pos) = self
131            .loaders
132            .iter()
133            .position(|l| (**l).as_any().is::<Prev>())
134        {
135            let prev_untyped = std::mem::replace(&mut self.loaders[pos], Box::new(new_loader));
136            prev_untyped
137                .into_any()
138                .downcast::<Prev>()
139                .ok()
140                .map(|boxed| *boxed)
141        } else {
142            None
143        }
144    }
145
146    /// Tries to find an instance of a resource loader of the given type `T.
147    pub fn find<T>(&self) -> Option<&T>
148    where
149        T: ResourceLoader,
150    {
151        self.loaders
152            .iter()
153            .find_map(|loader| (**loader).as_any().downcast_ref())
154    }
155
156    /// Tries to find an instance of a resource loader of the given type `T.
157    pub fn find_mut<T>(&mut self) -> Option<&mut T>
158    where
159        T: ResourceLoader,
160    {
161        self.loaders
162            .iter_mut()
163            .find_map(|loader| (**loader).as_any_mut().downcast_mut())
164    }
165
166    /// Returns total amount of resource loaders in the container.
167    pub fn len(&self) -> usize {
168        self.loaders.len()
169    }
170
171    /// Return `true` if the container contains no resource loaders.
172    pub fn is_empty(&self) -> bool {
173        self.loaders.is_empty()
174    }
175
176    /// Returns an iterator yielding shared references to "untyped" resource loaders.
177    pub fn iter(&self) -> impl Iterator<Item = &dyn ResourceLoader> {
178        self.loaders.iter().map(|boxed| &**boxed)
179    }
180
181    /// Returns an iterator yielding mutable references to "untyped" resource loaders.
182    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut dyn ResourceLoader> {
183        self.loaders.iter_mut().map(|boxed| &mut **boxed)
184    }
185}
186
187#[cfg(test)]
188mod test {
189    use super::*;
190
191    #[derive(Eq, PartialEq, Debug)]
192    struct MyResourceLoader;
193
194    impl ResourceLoader for MyResourceLoader {
195        fn extensions(&self) -> &[&str] {
196            &[]
197        }
198
199        fn data_type_uuid(&self) -> Uuid {
200            Default::default()
201        }
202
203        fn load(&self, _path: PathBuf, _io: Arc<dyn ResourceIo>) -> BoxedLoaderFuture {
204            todo!()
205        }
206    }
207
208    #[test]
209    fn resource_loader_container_new() {
210        let container = ResourceLoadersContainer::new();
211        assert!(container.loaders.is_empty());
212
213        let container = ResourceLoadersContainer::default();
214        assert!(container.loaders.is_empty());
215    }
216
217    #[test]
218    fn resource_loader_container_set() {
219        let mut container = ResourceLoadersContainer::new();
220        let res = container.set(MyResourceLoader);
221        let res2 = container.set(MyResourceLoader);
222        assert_eq!(res, None);
223        assert_eq!(res2, Some(MyResourceLoader));
224
225        assert_eq!(container.len(), 1);
226    }
227
228    #[test]
229    fn resource_loader_container_find() {
230        let mut container = ResourceLoadersContainer::new();
231
232        let res = container.find::<MyResourceLoader>();
233        assert_eq!(res, None);
234
235        container.set(MyResourceLoader);
236        let res = container.find::<MyResourceLoader>();
237
238        assert_eq!(res, Some(&MyResourceLoader));
239    }
240
241    #[test]
242    fn resource_loader_container_find_mut() {
243        let mut container = ResourceLoadersContainer::new();
244
245        let res = container.find_mut::<MyResourceLoader>();
246        assert_eq!(res, None);
247
248        container.set(MyResourceLoader);
249        let res = container.find_mut::<MyResourceLoader>();
250
251        assert_eq!(res, Some(&mut MyResourceLoader));
252    }
253
254    #[test]
255    fn resource_loader_container_getters() {
256        let mut container = ResourceLoadersContainer::new();
257        assert!(container.is_empty());
258        assert_eq!(container.len(), 0);
259
260        container.set(MyResourceLoader);
261        container.set(MyResourceLoader);
262        assert!(!container.is_empty());
263        assert_eq!(container.len(), 1);
264    }
265}