bevy_asset/
saver.rs

1use crate::transformer::TransformedAsset;
2use crate::{io::Writer, meta::Settings, Asset, ErasedLoadedAsset};
3use crate::{AssetLoader, Handle, LabeledAsset, UntypedHandle};
4use bevy_utils::{BoxedFuture, ConditionalSendFuture, CowArc, HashMap};
5use serde::{Deserialize, Serialize};
6use std::{borrow::Borrow, hash::Hash, ops::Deref};
7
8/// Saves an [`Asset`] of a given [`AssetSaver::Asset`] type. [`AssetSaver::OutputLoader`] will then be used to load the saved asset
9/// in the final deployed application. The saver should produce asset bytes in a format that [`AssetSaver::OutputLoader`] can read.
10pub trait AssetSaver: Send + Sync + 'static {
11    /// The top level [`Asset`] saved by this [`AssetSaver`].
12    type Asset: Asset;
13    /// The settings type used by this [`AssetSaver`].
14    type Settings: Settings + Default + Serialize + for<'a> Deserialize<'a>;
15    /// The type of [`AssetLoader`] used to load this [`Asset`]
16    type OutputLoader: AssetLoader;
17    /// The type of [error](`std::error::Error`) which could be encountered by this saver.
18    type Error: Into<Box<dyn std::error::Error + Send + Sync + 'static>>;
19
20    /// Saves the given runtime [`Asset`] by writing it to a byte format using `writer`. The passed in `settings` can influence how the
21    /// `asset` is saved.  
22    fn save<'a>(
23        &'a self,
24        writer: &'a mut Writer,
25        asset: SavedAsset<'a, Self::Asset>,
26        settings: &'a Self::Settings,
27    ) -> impl ConditionalSendFuture<
28        Output = Result<<Self::OutputLoader as AssetLoader>::Settings, Self::Error>,
29    >;
30}
31
32/// A type-erased dynamic variant of [`AssetSaver`] that allows callers to save assets without knowing the actual type of the [`AssetSaver`].
33pub trait ErasedAssetSaver: Send + Sync + 'static {
34    /// Saves the given runtime [`ErasedLoadedAsset`] by writing it to a byte format using `writer`. The passed in `settings` can influence how the
35    /// `asset` is saved.  
36    fn save<'a>(
37        &'a self,
38        writer: &'a mut Writer,
39        asset: &'a ErasedLoadedAsset,
40        settings: &'a dyn Settings,
41    ) -> BoxedFuture<'a, Result<(), Box<dyn std::error::Error + Send + Sync + 'static>>>;
42
43    /// The type name of the [`AssetSaver`].
44    fn type_name(&self) -> &'static str;
45}
46
47impl<S: AssetSaver> ErasedAssetSaver for S {
48    fn save<'a>(
49        &'a self,
50        writer: &'a mut Writer,
51        asset: &'a ErasedLoadedAsset,
52        settings: &'a dyn Settings,
53    ) -> BoxedFuture<'a, Result<(), Box<dyn std::error::Error + Send + Sync + 'static>>> {
54        Box::pin(async move {
55            let settings = settings
56                .downcast_ref::<S::Settings>()
57                .expect("AssetLoader settings should match the loader type");
58            let saved_asset = SavedAsset::<S::Asset>::from_loaded(asset).unwrap();
59            if let Err(err) = self.save(writer, saved_asset, settings).await {
60                return Err(err.into());
61            }
62            Ok(())
63        })
64    }
65    fn type_name(&self) -> &'static str {
66        std::any::type_name::<S>()
67    }
68}
69
70/// An [`Asset`] (and any labeled "sub assets") intended to be saved.
71pub struct SavedAsset<'a, A: Asset> {
72    value: &'a A,
73    labeled_assets: &'a HashMap<CowArc<'static, str>, LabeledAsset>,
74}
75
76impl<'a, A: Asset> Deref for SavedAsset<'a, A> {
77    type Target = A;
78
79    fn deref(&self) -> &Self::Target {
80        self.value
81    }
82}
83
84impl<'a, A: Asset> SavedAsset<'a, A> {
85    /// Creates a new [`SavedAsset`] from `asset` if its internal value matches `A`.
86    pub fn from_loaded(asset: &'a ErasedLoadedAsset) -> Option<Self> {
87        let value = asset.value.downcast_ref::<A>()?;
88        Some(SavedAsset {
89            value,
90            labeled_assets: &asset.labeled_assets,
91        })
92    }
93
94    /// Creates a new [`SavedAsset`] from the a [`TransformedAsset`]
95    pub fn from_transformed(asset: &'a TransformedAsset<A>) -> Self {
96        Self {
97            value: &asset.value,
98            labeled_assets: &asset.labeled_assets,
99        }
100    }
101
102    /// Retrieves the value of this asset.
103    #[inline]
104    pub fn get(&self) -> &'a A {
105        self.value
106    }
107
108    /// Returns the labeled asset, if it exists and matches this type.
109    pub fn get_labeled<B: Asset, Q>(&self, label: &Q) -> Option<SavedAsset<B>>
110    where
111        CowArc<'static, str>: Borrow<Q>,
112        Q: ?Sized + Hash + Eq,
113    {
114        let labeled = self.labeled_assets.get(label)?;
115        let value = labeled.asset.value.downcast_ref::<B>()?;
116        Some(SavedAsset {
117            value,
118            labeled_assets: &labeled.asset.labeled_assets,
119        })
120    }
121
122    /// Returns the type-erased labeled asset, if it exists and matches this type.
123    pub fn get_erased_labeled<Q>(&self, label: &Q) -> Option<&ErasedLoadedAsset>
124    where
125        CowArc<'static, str>: Borrow<Q>,
126        Q: ?Sized + Hash + Eq,
127    {
128        let labeled = self.labeled_assets.get(label)?;
129        Some(&labeled.asset)
130    }
131
132    /// Returns the [`UntypedHandle`] of the labeled asset with the provided 'label', if it exists.
133    pub fn get_untyped_handle<Q>(&self, label: &Q) -> Option<UntypedHandle>
134    where
135        CowArc<'static, str>: Borrow<Q>,
136        Q: ?Sized + Hash + Eq,
137    {
138        let labeled = self.labeled_assets.get(label)?;
139        Some(labeled.handle.clone())
140    }
141
142    /// Returns the [`Handle`] of the labeled asset with the provided 'label', if it exists and is an asset of type `B`
143    pub fn get_handle<Q, B: Asset>(&self, label: &Q) -> Option<Handle<B>>
144    where
145        CowArc<'static, str>: Borrow<Q>,
146        Q: ?Sized + Hash + Eq,
147    {
148        let labeled = self.labeled_assets.get(label)?;
149        if let Ok(handle) = labeled.handle.clone().try_typed::<B>() {
150            return Some(handle);
151        }
152        None
153    }
154
155    /// Iterate over all labels for "labeled assets" in the loaded asset
156    pub fn iter_labels(&self) -> impl Iterator<Item = &str> {
157        self.labeled_assets.keys().map(|s| &**s)
158    }
159}