diff --git a/crates/bevy_asset/src/server/info.rs b/crates/bevy_asset/src/server/info.rs index 37f727870be00..666f32847122d 100644 --- a/crates/bevy_asset/src/server/info.rs +++ b/crates/bevy_asset/src/server/info.rs @@ -314,6 +314,11 @@ impl AssetInfos { self.get_index_handle(ErasedAssetIndex::new(index, type_id)) } + pub(crate) fn get_path_ref(&self, index: ErasedAssetIndex) -> Option<&AssetPath<'static>> { + let info = self.infos.get(&index)?; + info.path.as_ref() + } + pub(crate) fn get_path_indices<'a>( &'a self, path: &'a AssetPath<'_>, diff --git a/crates/bevy_asset/src/server/mod.rs b/crates/bevy_asset/src/server/mod.rs index 3ae2ee1af6971..516527844a487 100644 --- a/crates/bevy_asset/src/server/mod.rs +++ b/crates/bevy_asset/src/server/mod.rs @@ -1365,6 +1365,31 @@ impl AssetServer { Some(info.path.as_ref()?.clone()) } + /// Returns a reference to the path for the given `id`, if it has one. + /// + /// This method is useful when you need to access the path without cloning it, + /// which can be more efficient when working with many asset handles. + /// + /// Note: The returned reference is tied to a temporary lock guard that lives + /// as long as the returned value is in scope. For more complex use cases, + /// consider using [`AssetServer::get_path`] instead. + pub fn get_path_ref<'a>( + &'a self, + id: impl Into, + ) -> Option> + 'a> { + let index = id.into().try_into().ok()?; + let infos = self.read_infos(); + + if infos.get_path_ref(index).is_some() { + Some(AssetPathRef { + _guard: infos, + index, + }) + } else { + None + } + } + /// Returns the [`AssetServerMode`] this server is currently in. pub fn mode(&self) -> AssetServerMode { self.data.mode @@ -2133,6 +2158,27 @@ fn format_missing_asset_ext(exts: &[String]) -> String { } } +/// A reference to an asset path that keeps the internal lock alive. +/// +/// This type is returned by [`AssetServer::get_path_ref`] and allows +/// accessing the path without cloning it, while keeping the necessary +/// read lock alive for the duration of its lifetime. +pub struct AssetPathRef<'a> { + _guard: RwLockReadGuard<'a, AssetInfos>, + index: ErasedAssetIndex, +} + +impl<'a> core::ops::Deref for AssetPathRef<'a> { + type Target = AssetPath<'static>; + + fn deref(&self) -> &Self::Target { + // SAFETY: We checked in get_path_ref that the path exists, so this unwrap is safe + self._guard + .get_path_ref(self.index) + .expect("AssetPathRef should only be created when path exists") + } +} + impl core::fmt::Debug for AssetServer { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("AssetServer") diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index 2488dbcfd0b47..478bae46e010a 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -500,6 +500,16 @@ impl World { /// Runs a cached system, registering it if necessary. /// + /// # Type Inference Note + /// If the system returns `()`, you may need to explicitly constrain the output + /// type for error handling: + /// + /// ```rust + /// () = world.run_system_cached(my_system)?; + /// ``` + /// + /// Without this, Rust may fail to infer the system’s output type and produce + /// a `IntoResult` inference error. /// See [`World::register_system_cached`] for more information. pub fn run_system_cached + 'static>( &mut self, @@ -508,8 +518,21 @@ impl World { self.run_system_cached_with(system, ()) } - /// Runs a cached system with an input, registering it if necessary. + /// Runs a cached system with the provided input, registering it if necessary. + /// + /// This is a more general version of [`World::run_system_cached`], allowing + /// callers to supply an explicit system input. + /// + /// # Type Inference Note + /// If the system returns `()`, you may need to explicitly constrain the + /// output type for proper error inference: + /// + /// ```rust + /// () = world.run_system_cached_with(my_system, input)?; + /// ``` /// + /// Without this, Rust may fail to infer the system’s output type and produce + /// a `IntoResult` inference error. /// See [`World::register_system_cached`] for more information. pub fn run_system_cached_with( &mut self,