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