Allow returning an error from labeled_asset_scope. (#19449)

# Objective

- `LoadContext::labeled_asset_scope` cannot return errors back to the
asset loader. This means users that need errors need to fall back to
using the raw `begin_labeled_asset` and `add_loaded_labeled_asset`,
which is more error-prone.

## Solution

- Allow returning a (generic) error from `labeled_asset_scope`.
- This has the unfortunate side effect that closures which don't return
any errors need to A) return Ok at the end, B) need to specify an error
type (e.g., `()`).

---

## Showcase

```rust
// impl AssetLoader for MyLoader
let handle = load_context.labeled_asset_scope("MySubasset", |mut load_context| {
  if !some_precondition {
    return Err(ThingsDontMakeSenseError);
  }
  let handle = load_context.add_labeled_asset("MySubasset/Other", SomeOtherThing(456));
  Ok(Something{ id: 123, handle })
})?;
```
This commit is contained in:
andriyDev 2025-06-03 17:00:32 -07:00 committed by GitHub
parent 5561b40bdf
commit 723b52abd3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 237 additions and 197 deletions

View File

@ -388,15 +388,15 @@ impl<'a> LoadContext<'a> {
/// result with [`LoadContext::add_labeled_asset`]. /// result with [`LoadContext::add_labeled_asset`].
/// ///
/// See [`AssetPath`] for more on labeled assets. /// See [`AssetPath`] for more on labeled assets.
pub fn labeled_asset_scope<A: Asset>( pub fn labeled_asset_scope<A: Asset, E>(
&mut self, &mut self,
label: String, label: String,
load: impl FnOnce(&mut LoadContext) -> A, load: impl FnOnce(&mut LoadContext) -> Result<A, E>,
) -> Handle<A> { ) -> Result<Handle<A>, E> {
let mut context = self.begin_labeled_asset(); let mut context = self.begin_labeled_asset();
let asset = load(&mut context); let asset = load(&mut context)?;
let loaded_asset = context.finish(asset); let loaded_asset = context.finish(asset);
self.add_loaded_labeled_asset(label, loaded_asset) Ok(self.add_loaded_labeled_asset(label, loaded_asset))
} }
/// This will add the given `asset` as a "labeled [`Asset`]" with the `label` label. /// This will add the given `asset` as a "labeled [`Asset`]" with the `label` label.
@ -410,7 +410,8 @@ impl<'a> LoadContext<'a> {
/// ///
/// See [`AssetPath`] for more on labeled assets. /// See [`AssetPath`] for more on labeled assets.
pub fn add_labeled_asset<A: Asset>(&mut self, label: String, asset: A) -> Handle<A> { pub fn add_labeled_asset<A: Asset>(&mut self, label: String, asset: A) -> Handle<A> {
self.labeled_asset_scope(label, |_| asset) self.labeled_asset_scope(label, |_| Ok::<_, ()>(asset))
.expect("the closure returns Ok")
} }
/// Add a [`LoadedAsset`] that is a "labeled sub asset" of the root path of this load context. /// Add a [`LoadedAsset`] that is a "labeled sub asset" of the root path of this load context.

View File

@ -1043,7 +1043,8 @@ fn load_material(
is_scale_inverted: bool, is_scale_inverted: bool,
) -> Handle<StandardMaterial> { ) -> Handle<StandardMaterial> {
let material_label = material_label(material, is_scale_inverted); let material_label = material_label(material, is_scale_inverted);
load_context.labeled_asset_scope(material_label.to_string(), |load_context| { load_context
.labeled_asset_scope::<_, ()>(material_label.to_string(), |load_context| {
let pbr = material.pbr_metallic_roughness(); let pbr = material.pbr_metallic_roughness();
// TODO: handle missing label handle errors here? // TODO: handle missing label handle errors here?
@ -1106,8 +1107,11 @@ fn load_material(
}); });
#[cfg(feature = "pbr_transmission_textures")] #[cfg(feature = "pbr_transmission_textures")]
let (specular_transmission, specular_transmission_channel, specular_transmission_texture) = let (
material specular_transmission,
specular_transmission_channel,
specular_transmission_texture,
) = material
.transmission() .transmission()
.map_or((0.0, UvChannel::Uv0, None), |transmission| { .map_or((0.0, UvChannel::Uv0, None), |transmission| {
let specular_transmission_channel = transmission let specular_transmission_channel = transmission
@ -1191,7 +1195,7 @@ fn load_material(
let base_emissive = LinearRgba::rgb(emissive[0], emissive[1], emissive[2]); let base_emissive = LinearRgba::rgb(emissive[0], emissive[1], emissive[2]);
let emissive = base_emissive * material.emissive_strength().unwrap_or(1.0); let emissive = base_emissive * material.emissive_strength().unwrap_or(1.0);
StandardMaterial { Ok(StandardMaterial {
base_color: Color::linear_rgba(color[0], color[1], color[2], color[3]), base_color: Color::linear_rgba(color[0], color[1], color[2], color[3]),
base_color_channel, base_color_channel,
base_color_texture, base_color_texture,
@ -1235,8 +1239,9 @@ fn load_material(
alpha_mode: alpha_mode(material), alpha_mode: alpha_mode(material),
uv_transform, uv_transform,
clearcoat: clearcoat.clearcoat_factor.unwrap_or_default() as f32, clearcoat: clearcoat.clearcoat_factor.unwrap_or_default() as f32,
clearcoat_perceptual_roughness: clearcoat.clearcoat_roughness_factor.unwrap_or_default() clearcoat_perceptual_roughness: clearcoat
as f32, .clearcoat_roughness_factor
.unwrap_or_default() as f32,
#[cfg(feature = "pbr_multi_layer_material_textures")] #[cfg(feature = "pbr_multi_layer_material_textures")]
clearcoat_channel: clearcoat.clearcoat_channel, clearcoat_channel: clearcoat.clearcoat_channel,
#[cfg(feature = "pbr_multi_layer_material_textures")] #[cfg(feature = "pbr_multi_layer_material_textures")]
@ -1263,7 +1268,9 @@ fn load_material(
#[cfg(feature = "pbr_specular_textures")] #[cfg(feature = "pbr_specular_textures")]
specular_texture: specular.specular_texture, specular_texture: specular.specular_texture,
specular_tint: match specular.specular_color_factor { specular_tint: match specular.specular_color_factor {
Some(color) => Color::linear_rgb(color[0] as f32, color[1] as f32, color[2] as f32), Some(color) => {
Color::linear_rgb(color[0] as f32, color[1] as f32, color[2] as f32)
}
None => Color::WHITE, None => Color::WHITE,
}, },
#[cfg(feature = "pbr_specular_textures")] #[cfg(feature = "pbr_specular_textures")]
@ -1271,8 +1278,9 @@ fn load_material(
#[cfg(feature = "pbr_specular_textures")] #[cfg(feature = "pbr_specular_textures")]
specular_tint_texture: specular.specular_color_texture, specular_tint_texture: specular.specular_color_texture,
..Default::default() ..Default::default()
}
}) })
})
.unwrap()
} }
/// Loads a glTF node. /// Loads a glTF node.

View File

@ -0,0 +1,31 @@
---
title: `labeled_asset_scope` can now return errors.
pull_requests: [19449]
---
`labeled_asset_scope` now returns a user-specified error type based on their closure. Previously,
users would need to fall back to `begin_labeled_asset` and `add_loaded_labeled_asset` to handle
errors, which is more error-prone. Consider migrating to use `labeled_asset_scope` if this was you!
However, `labeled_asset_scope` closures that don't return errors now needs to A) return Ok, and B)
specify an error type.
If your code previously looked like this:
```rust
labeled_asset_scope(label, |mut load_context| {
let my_asset = ...;
my_asset
});
```
You can migrate it to:
```rust
labeled_asset_scope::<_, ()>(label, |mut load_context| {
let my_asset = ...;
Ok(my_asset)
}).unwrap();
```