Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions crates/bevy_asset/src/server/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<'_>,
Expand Down
46 changes: 46 additions & 0 deletions crates/bevy_asset/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<UntypedAssetId>,
) -> Option<impl core::ops::Deref<Target = AssetPath<'static>> + '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
Expand Down Expand Up @@ -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")
Expand Down
25 changes: 24 additions & 1 deletion crates/bevy_ecs/src/system/system_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<O: 'static, M, S: IntoSystem<(), O, M> + 'static>(
&mut self,
Expand All @@ -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<I, O, M, S>(
&mut self,
Expand Down
Loading