1use crate::{
5 io::Reader,
6 meta::{loader_settings_meta_transform, MetaTransform, Settings},
7 Asset, AssetPath, ErasedAssetIndex, ErasedAssetLoader, ErasedLoadedAsset, Handle, LoadContext,
8 LoadDirectError, LoadedAsset, LoadedUntypedAsset, RequestedHandleTypeMismatchError,
9 UntypedHandle,
10};
11use alloc::{borrow::ToOwned, boxed::Box, sync::Arc};
12use core::any::{type_name, TypeId};
13use std::path::Path;
14use tracing::error;
15
16enum ReaderRef<'a> {
18 Borrowed(&'a mut dyn Reader),
19 Boxed(Box<dyn Reader + 'a>),
20}
21
22impl ReaderRef<'_> {
23 pub fn as_mut(&mut self) -> &mut dyn Reader {
24 match self {
25 ReaderRef::Borrowed(r) => &mut **r,
26 ReaderRef::Boxed(b) => &mut **b,
27 }
28 }
29}
30
31pub struct NestedLoadBuilder<'ctx, 'builder> {
33 load_context: &'builder mut LoadContext<'ctx>,
34 meta_transform: Option<MetaTransform>,
37 override_unapproved: bool,
39}
40
41impl<'ctx, 'builder> NestedLoadBuilder<'ctx, 'builder> {
42 pub(crate) fn new(load_context: &'builder mut LoadContext<'ctx>) -> Self {
43 NestedLoadBuilder {
44 load_context,
45 meta_transform: None,
46 override_unapproved: false,
47 }
48 }
49}
50
51impl<'ctx, 'builder> NestedLoadBuilder<'ctx, 'builder> {
52 #[must_use]
63 pub fn with_settings<S: Settings>(
64 mut self,
65 settings: impl Fn(&mut S) + Send + Sync + 'static,
66 ) -> Self {
67 let new_transform = loader_settings_meta_transform(settings);
68 if let Some(prev_transform) = self.meta_transform.take() {
69 self.meta_transform = Some(Box::new(move |meta| {
70 prev_transform(meta);
71 new_transform(meta);
72 }));
73 } else {
74 self.meta_transform = Some(new_transform);
75 }
76 self
77 }
78
79 #[must_use = "the load doesn't start until LoadBuilder has been consumed"]
83 pub fn override_unapproved(mut self) -> Self {
84 self.override_unapproved = true;
85 self
86 }
87
88 pub fn load<'a, A: Asset>(self, path: impl Into<AssetPath<'a>>) -> Handle<A> {
93 self.load_internal(TypeId::of::<A>(), Some(type_name::<A>()), path.into())
97 .typed_debug_checked()
98 }
99
100 pub fn load_erased<'a>(self, type_id: TypeId, path: impl Into<AssetPath<'a>>) -> UntypedHandle {
105 self.load_internal(type_id, None, path.into())
106 }
107
108 pub fn load_untyped<'a>(self, path: impl Into<AssetPath<'a>>) -> Handle<LoadedUntypedAsset> {
114 let path = path.into().to_owned();
115 if path.path() == Path::new("") {
116 error!("Attempted to load an asset with an empty path \"{path}\"!");
117 return Handle::default();
118 }
119 let handle = if self.load_context.should_load_dependencies {
120 self.load_context
121 .asset_server
122 .load_unknown_type_with_meta_transform(
123 path,
124 self.meta_transform,
125 (),
126 self.override_unapproved,
127 )
128 } else {
129 self.load_context
130 .asset_server
131 .get_or_create_path_handle(path, self.meta_transform)
132 };
133 if let Ok(index) = ErasedAssetIndex::try_from(&handle) {
137 self.load_context.dependencies.insert(index);
138 }
139 handle
140 }
141
142 pub async fn load_value<'a, A: Asset>(
146 self,
147 path: impl Into<AssetPath<'a>>,
148 ) -> Result<LoadedAsset<A>, LoadDirectError> {
149 self.load_typed_value_internal(path.into().into_owned(), None)
150 .await
151 }
152
153 pub async fn load_erased_value<'a>(
157 self,
158 type_id: TypeId,
159 path: impl Into<AssetPath<'a>>,
160 ) -> Result<ErasedLoadedAsset, LoadDirectError> {
161 self.load_value_internal(Some(type_id), &path.into().into_owned(), None)
162 .await
163 .map(|(_, asset)| asset)
164 }
165
166 pub async fn load_untyped_value<'a>(
171 self,
172 path: impl Into<AssetPath<'a>>,
173 ) -> Result<ErasedLoadedAsset, LoadDirectError> {
174 self.load_value_internal(None, &path.into().into_owned(), None)
175 .await
176 .map(|(_, asset)| asset)
177 }
178
179 pub async fn load_value_from_reader<'a, A: Asset>(
185 self,
186 path: impl Into<AssetPath<'a>>,
187 reader: &'builder mut dyn Reader,
188 ) -> Result<LoadedAsset<A>, LoadDirectError> {
189 self.load_typed_value_internal(path.into().into_owned(), Some(reader))
190 .await
191 }
192
193 pub async fn load_erased_value_from_reader<'a>(
199 self,
200 type_id: TypeId,
201 path: impl Into<AssetPath<'a>>,
202 reader: &'builder mut dyn Reader,
203 ) -> Result<ErasedLoadedAsset, LoadDirectError> {
204 self.load_value_internal(Some(type_id), &path.into().into_owned(), Some(reader))
205 .await
206 .map(|(_, asset)| asset)
207 }
208
209 pub async fn load_untyped_value_from_reader<'a>(
216 self,
217 path: impl Into<AssetPath<'a>>,
218 reader: &'builder mut dyn Reader,
219 ) -> Result<ErasedLoadedAsset, LoadDirectError> {
220 self.load_value_internal(None, &path.into().into_owned(), Some(reader))
221 .await
222 .map(|(_, asset)| asset)
223 }
224
225 fn load_internal<'a>(
228 self,
229 type_id: TypeId,
230 type_name: Option<&str>,
231 path: AssetPath<'a>,
232 ) -> UntypedHandle {
233 let path = path.to_owned();
234 if path.path() == Path::new("") {
235 error!("Attempted to load an asset with an empty path \"{path}\"!");
236 return UntypedHandle::default_for_type(type_id);
237 }
238 let handle = if self.load_context.should_load_dependencies {
239 self.load_context.asset_server.load_with_meta_transform(
240 path,
241 type_id,
242 type_name,
243 self.meta_transform,
244 (),
245 self.override_unapproved,
246 )
247 } else {
248 self.load_context
249 .asset_server
250 .get_or_create_path_handle_erased(path, type_id, type_name, self.meta_transform)
251 };
252 if let Ok(index) = ErasedAssetIndex::try_from(&handle) {
256 self.load_context.dependencies.insert(index);
257 }
258 handle
259 }
260
261 async fn load_value_internal(
267 self,
268 type_id: Option<TypeId>,
269 path: &AssetPath<'static>,
270 reader: Option<&'builder mut dyn Reader>,
271 ) -> Result<(Arc<dyn ErasedAssetLoader>, ErasedLoadedAsset), LoadDirectError> {
272 if path.path() == Path::new("") {
273 error!("Attempted to load an asset with an empty path \"{path}\"!");
274 return Err(LoadDirectError::EmptyPath(path.clone_owned()));
275 }
276 if path.label().is_some() {
277 return Err(LoadDirectError::RequestedSubasset(path.clone()));
278 }
279 self.load_context
280 .asset_server
281 .write_infos()
282 .stats
283 .started_load_tasks += 1;
284 let (mut meta, loader, mut reader) = if let Some(reader) = reader {
285 let loader = if let Some(type_id) = type_id {
286 self.load_context
287 .asset_server
288 .get_asset_loader_with_asset_type_id(type_id)
289 .await
290 .map_err(|error| LoadDirectError::LoadError {
291 dependency: path.clone(),
292 error: Box::new(error.into()),
293 })?
294 } else {
295 self.load_context
296 .asset_server
297 .get_path_asset_loader(path)
298 .await
299 .map_err(|error| LoadDirectError::LoadError {
300 dependency: path.clone(),
301 error: Box::new(error.into()),
302 })?
303 };
304 let meta = loader.default_meta();
305 (meta, loader, ReaderRef::Borrowed(reader))
306 } else {
307 let (meta, loader, reader) = self
308 .load_context
309 .asset_server
310 .get_meta_loader_and_reader(path, type_id)
311 .await
312 .map_err(|error| LoadDirectError::LoadError {
313 dependency: path.clone(),
314 error: Box::new(error),
315 })?;
316 (meta, loader, ReaderRef::Boxed(reader))
317 };
318
319 if let Some(meta_transform) = self.meta_transform {
320 meta_transform(&mut *meta);
321 }
322
323 let asset = self
324 .load_context
325 .load_direct_internal(
326 path.clone(),
327 meta.loader_settings().expect("meta corresponds to a load"),
328 &*loader,
329 reader.as_mut(),
330 meta.processed_info().as_ref(),
331 )
332 .await?;
333 Ok((loader, asset))
334 }
335
336 async fn load_typed_value_internal<A: Asset>(
339 self,
340 path: AssetPath<'static>,
341 reader: Option<&'builder mut dyn Reader>,
342 ) -> Result<LoadedAsset<A>, LoadDirectError> {
343 self.load_value_internal(Some(TypeId::of::<A>()), &path, reader)
344 .await
345 .and_then(move |(loader, untyped_asset)| {
346 untyped_asset
347 .downcast::<A>()
348 .map_err(|_| LoadDirectError::LoadError {
349 dependency: path.clone(),
350 error: Box::new(
351 Box::new(RequestedHandleTypeMismatchError {
352 path,
353 requested: TypeId::of::<A>(),
354 actual_asset_name: loader.asset_type_name(),
355 loader_name: loader.type_path(),
356 })
357 .into(),
358 ),
359 })
360 })
361 }
362}