bevy/crates/bevy_asset/src/saver.rs
Kristoffer Søholm 73af2b7d29
Cleanup unneeded lifetimes in bevy_asset (#15546)
# Objective

Fixes #15541

A bunch of lifetimes were added during the Assets V2 rework, but after
moving to async traits in #12550 they can be elided. That PR mentions
that this might be the case, but apparently it wasn't followed up on at
the time.

~~I ended up grepping for `<'a` and finding a similar case in
`bevy_reflect` which I also fixed.~~ (edit: that one was needed
apparently)

Note that elided lifetimes are unstable in `impl Trait`. If that gets
stabilized then we can elide even more.

## Solution

Remove the extra lifetimes.

## Testing

Everything still compiles. If I have messed something up there is a
small risk that some user code stops compiling, but all the examples
still work at least.

---

## Migration Guide

The traits `AssetLoader`, `AssetSaver` and `Process` traits from
`bevy_asset` now use elided lifetimes. If you implement these then
remove the named lifetime.
2024-09-30 21:54:59 +00:00

166 lines
6.0 KiB
Rust

use crate::{
io::Writer, meta::Settings, transformer::TransformedAsset, Asset, AssetLoader,
ErasedLoadedAsset, Handle, LabeledAsset, UntypedHandle,
};
use atomicow::CowArc;
use bevy_utils::{BoxedFuture, ConditionalSendFuture, HashMap};
use core::{borrow::Borrow, hash::Hash, ops::Deref};
use serde::{Deserialize, Serialize};
/// Saves an [`Asset`] of a given [`AssetSaver::Asset`] type. [`AssetSaver::OutputLoader`] will then be used to load the saved asset
/// in the final deployed application. The saver should produce asset bytes in a format that [`AssetSaver::OutputLoader`] can read.
///
/// This trait is generally used in concert with [`AssetWriter`](crate::io::AssetWriter) to write assets as bytes.
///
/// For a complementary version of this trait that can load assets, see [`AssetLoader`].
pub trait AssetSaver: Send + Sync + 'static {
/// The top level [`Asset`] saved by this [`AssetSaver`].
type Asset: Asset;
/// The settings type used by this [`AssetSaver`].
type Settings: Settings + Default + Serialize + for<'a> Deserialize<'a>;
/// The type of [`AssetLoader`] used to load this [`Asset`]
type OutputLoader: AssetLoader;
/// The type of [error](`std::error::Error`) which could be encountered by this saver.
type Error: Into<Box<dyn core::error::Error + Send + Sync + 'static>>;
/// Saves the given runtime [`Asset`] by writing it to a byte format using `writer`. The passed in `settings` can influence how the
/// `asset` is saved.
fn save(
&self,
writer: &mut Writer,
asset: SavedAsset<'_, Self::Asset>,
settings: &Self::Settings,
) -> impl ConditionalSendFuture<
Output = Result<<Self::OutputLoader as AssetLoader>::Settings, Self::Error>,
>;
}
/// A type-erased dynamic variant of [`AssetSaver`] that allows callers to save assets without knowing the actual type of the [`AssetSaver`].
pub trait ErasedAssetSaver: Send + Sync + 'static {
/// Saves the given runtime [`ErasedLoadedAsset`] by writing it to a byte format using `writer`. The passed in `settings` can influence how the
/// `asset` is saved.
fn save<'a>(
&'a self,
writer: &'a mut Writer,
asset: &'a ErasedLoadedAsset,
settings: &'a dyn Settings,
) -> BoxedFuture<'a, Result<(), Box<dyn core::error::Error + Send + Sync + 'static>>>;
/// The type name of the [`AssetSaver`].
fn type_name(&self) -> &'static str;
}
impl<S: AssetSaver> ErasedAssetSaver for S {
fn save<'a>(
&'a self,
writer: &'a mut Writer,
asset: &'a ErasedLoadedAsset,
settings: &'a dyn Settings,
) -> BoxedFuture<'a, Result<(), Box<dyn core::error::Error + Send + Sync + 'static>>> {
Box::pin(async move {
let settings = settings
.downcast_ref::<S::Settings>()
.expect("AssetLoader settings should match the loader type");
let saved_asset = SavedAsset::<S::Asset>::from_loaded(asset).unwrap();
if let Err(err) = self.save(writer, saved_asset, settings).await {
return Err(err.into());
}
Ok(())
})
}
fn type_name(&self) -> &'static str {
core::any::type_name::<S>()
}
}
/// An [`Asset`] (and any labeled "sub assets") intended to be saved.
pub struct SavedAsset<'a, A: Asset> {
value: &'a A,
labeled_assets: &'a HashMap<CowArc<'static, str>, LabeledAsset>,
}
impl<'a, A: Asset> Deref for SavedAsset<'a, A> {
type Target = A;
fn deref(&self) -> &Self::Target {
self.value
}
}
impl<'a, A: Asset> SavedAsset<'a, A> {
/// Creates a new [`SavedAsset`] from `asset` if its internal value matches `A`.
pub fn from_loaded(asset: &'a ErasedLoadedAsset) -> Option<Self> {
let value = asset.value.downcast_ref::<A>()?;
Some(SavedAsset {
value,
labeled_assets: &asset.labeled_assets,
})
}
/// Creates a new [`SavedAsset`] from the a [`TransformedAsset`]
pub fn from_transformed(asset: &'a TransformedAsset<A>) -> Self {
Self {
value: &asset.value,
labeled_assets: &asset.labeled_assets,
}
}
/// Retrieves the value of this asset.
#[inline]
pub fn get(&self) -> &'a A {
self.value
}
/// Returns the labeled asset, if it exists and matches this type.
pub fn get_labeled<B: Asset, Q>(&self, label: &Q) -> Option<SavedAsset<B>>
where
CowArc<'static, str>: Borrow<Q>,
Q: ?Sized + Hash + Eq,
{
let labeled = self.labeled_assets.get(label)?;
let value = labeled.asset.value.downcast_ref::<B>()?;
Some(SavedAsset {
value,
labeled_assets: &labeled.asset.labeled_assets,
})
}
/// Returns the type-erased labeled asset, if it exists and matches this type.
pub fn get_erased_labeled<Q>(&self, label: &Q) -> Option<&ErasedLoadedAsset>
where
CowArc<'static, str>: Borrow<Q>,
Q: ?Sized + Hash + Eq,
{
let labeled = self.labeled_assets.get(label)?;
Some(&labeled.asset)
}
/// Returns the [`UntypedHandle`] of the labeled asset with the provided 'label', if it exists.
pub fn get_untyped_handle<Q>(&self, label: &Q) -> Option<UntypedHandle>
where
CowArc<'static, str>: Borrow<Q>,
Q: ?Sized + Hash + Eq,
{
let labeled = self.labeled_assets.get(label)?;
Some(labeled.handle.clone())
}
/// Returns the [`Handle`] of the labeled asset with the provided 'label', if it exists and is an asset of type `B`
pub fn get_handle<Q, B: Asset>(&self, label: &Q) -> Option<Handle<B>>
where
CowArc<'static, str>: Borrow<Q>,
Q: ?Sized + Hash + Eq,
{
let labeled = self.labeled_assets.get(label)?;
if let Ok(handle) = labeled.handle.clone().try_typed::<B>() {
return Some(handle);
}
None
}
/// Iterate over all labels for "labeled assets" in the loaded asset
pub fn iter_labels(&self) -> impl Iterator<Item = &str> {
self.labeled_assets.keys().map(|s| &**s)
}
}