bevy_asset/
transformer.rs

1use crate::{meta::Settings, Asset, ErasedLoadedAsset, Handle, LabeledAsset, UntypedHandle};
2use atomicow::CowArc;
3use bevy_utils::{ConditionalSendFuture, HashMap};
4use core::{
5    borrow::Borrow,
6    convert::Infallible,
7    hash::Hash,
8    marker::PhantomData,
9    ops::{Deref, DerefMut},
10};
11use serde::{Deserialize, Serialize};
12
13/// Transforms an [`Asset`] of a given [`AssetTransformer::AssetInput`] type to an [`Asset`] of [`AssetTransformer::AssetOutput`] type.
14///
15/// This trait is commonly used in association with [`LoadTransformAndSave`](crate::processor::LoadTransformAndSave) to accomplish common asset pipeline workflows.
16pub trait AssetTransformer: Send + Sync + 'static {
17    /// The [`Asset`] type which this [`AssetTransformer`] takes as and input.
18    type AssetInput: Asset;
19    /// The [`Asset`] type which this [`AssetTransformer`] outputs.
20    type AssetOutput: Asset;
21    /// The settings type used by this [`AssetTransformer`].
22    type Settings: Settings + Default + Serialize + for<'a> Deserialize<'a>;
23    /// The type of [error](`std::error::Error`) which could be encountered by this transformer.
24    type Error: Into<Box<dyn core::error::Error + Send + Sync + 'static>>;
25
26    /// Transforms the given [`TransformedAsset`] to [`AssetTransformer::AssetOutput`].
27    /// The [`TransformedAsset`]'s `labeled_assets` can be altered to add new Labeled Sub-Assets
28    /// The passed in `settings` can influence how the `asset` is transformed
29    fn transform<'a>(
30        &'a self,
31        asset: TransformedAsset<Self::AssetInput>,
32        settings: &'a Self::Settings,
33    ) -> impl ConditionalSendFuture<Output = Result<TransformedAsset<Self::AssetOutput>, Self::Error>>;
34}
35
36/// An [`Asset`] (and any "sub assets") intended to be transformed
37pub struct TransformedAsset<A: Asset> {
38    pub(crate) value: A,
39    pub(crate) labeled_assets: HashMap<CowArc<'static, str>, LabeledAsset>,
40}
41
42impl<A: Asset> Deref for TransformedAsset<A> {
43    type Target = A;
44    fn deref(&self) -> &Self::Target {
45        &self.value
46    }
47}
48
49impl<A: Asset> DerefMut for TransformedAsset<A> {
50    fn deref_mut(&mut self) -> &mut Self::Target {
51        &mut self.value
52    }
53}
54
55impl<A: Asset> TransformedAsset<A> {
56    /// Creates a new [`TransformedAsset`] from `asset` if its internal value matches `A`.
57    pub fn from_loaded(asset: ErasedLoadedAsset) -> Option<Self> {
58        if let Ok(value) = asset.value.downcast::<A>() {
59            return Some(TransformedAsset {
60                value: *value,
61                labeled_assets: asset.labeled_assets,
62            });
63        }
64        None
65    }
66    /// Creates a new [`TransformedAsset`] from `asset`, transferring the `labeled_assets` from this [`TransformedAsset`] to the new one
67    pub fn replace_asset<B: Asset>(self, asset: B) -> TransformedAsset<B> {
68        TransformedAsset {
69            value: asset,
70            labeled_assets: self.labeled_assets,
71        }
72    }
73    /// Takes the labeled assets from `labeled_source` and places them in this [`TransformedAsset`]
74    pub fn take_labeled_assets<B: Asset>(&mut self, labeled_source: TransformedAsset<B>) {
75        self.labeled_assets = labeled_source.labeled_assets;
76    }
77    /// Retrieves the value of this asset.
78    #[inline]
79    pub fn get(&self) -> &A {
80        &self.value
81    }
82    /// Mutably retrieves the value of this asset.
83    #[inline]
84    pub fn get_mut(&mut self) -> &mut A {
85        &mut self.value
86    }
87    /// Returns the labeled asset, if it exists and matches this type.
88    pub fn get_labeled<B: Asset, Q>(&mut self, label: &Q) -> Option<TransformedSubAsset<B>>
89    where
90        CowArc<'static, str>: Borrow<Q>,
91        Q: ?Sized + Hash + Eq,
92    {
93        let labeled = self.labeled_assets.get_mut(label)?;
94        let value = labeled.asset.value.downcast_mut::<B>()?;
95        Some(TransformedSubAsset {
96            value,
97            labeled_assets: &mut labeled.asset.labeled_assets,
98        })
99    }
100    /// Returns the type-erased labeled asset, if it exists and matches this type.
101    pub fn get_erased_labeled<Q>(&self, label: &Q) -> Option<&ErasedLoadedAsset>
102    where
103        CowArc<'static, str>: Borrow<Q>,
104        Q: ?Sized + Hash + Eq,
105    {
106        let labeled = self.labeled_assets.get(label)?;
107        Some(&labeled.asset)
108    }
109    /// Returns the [`UntypedHandle`] of the labeled asset with the provided 'label', if it exists.
110    pub fn get_untyped_handle<Q>(&self, label: &Q) -> Option<UntypedHandle>
111    where
112        CowArc<'static, str>: Borrow<Q>,
113        Q: ?Sized + Hash + Eq,
114    {
115        let labeled = self.labeled_assets.get(label)?;
116        Some(labeled.handle.clone())
117    }
118    /// Returns the [`Handle`] of the labeled asset with the provided 'label', if it exists and is an asset of type `B`
119    pub fn get_handle<Q, B: Asset>(&self, label: &Q) -> Option<Handle<B>>
120    where
121        CowArc<'static, str>: Borrow<Q>,
122        Q: ?Sized + Hash + Eq,
123    {
124        let labeled = self.labeled_assets.get(label)?;
125        if let Ok(handle) = labeled.handle.clone().try_typed::<B>() {
126            return Some(handle);
127        }
128        None
129    }
130    /// Adds `asset` as a labeled sub asset using `label` and `handle`
131    pub fn insert_labeled(
132        &mut self,
133        label: impl Into<CowArc<'static, str>>,
134        handle: impl Into<UntypedHandle>,
135        asset: impl Into<ErasedLoadedAsset>,
136    ) {
137        let labeled = LabeledAsset {
138            asset: asset.into(),
139            handle: handle.into(),
140        };
141        self.labeled_assets.insert(label.into(), labeled);
142    }
143    /// Iterate over all labels for "labeled assets" in the loaded asset
144    pub fn iter_labels(&self) -> impl Iterator<Item = &str> {
145        self.labeled_assets.keys().map(|s| &**s)
146    }
147}
148
149/// A labeled sub-asset of [`TransformedAsset`]
150pub struct TransformedSubAsset<'a, A: Asset> {
151    value: &'a mut A,
152    labeled_assets: &'a mut HashMap<CowArc<'static, str>, LabeledAsset>,
153}
154
155impl<'a, A: Asset> Deref for TransformedSubAsset<'a, A> {
156    type Target = A;
157    fn deref(&self) -> &Self::Target {
158        self.value
159    }
160}
161
162impl<'a, A: Asset> DerefMut for TransformedSubAsset<'a, A> {
163    fn deref_mut(&mut self) -> &mut Self::Target {
164        self.value
165    }
166}
167
168impl<'a, A: Asset> TransformedSubAsset<'a, A> {
169    /// Creates a new [`TransformedSubAsset`] from `asset` if its internal value matches `A`.
170    pub fn from_loaded(asset: &'a mut ErasedLoadedAsset) -> Option<Self> {
171        let value = asset.value.downcast_mut::<A>()?;
172        Some(TransformedSubAsset {
173            value,
174            labeled_assets: &mut asset.labeled_assets,
175        })
176    }
177    /// Retrieves the value of this asset.
178    #[inline]
179    pub fn get(&self) -> &A {
180        self.value
181    }
182    /// Mutably retrieves the value of this asset.
183    #[inline]
184    pub fn get_mut(&mut self) -> &mut A {
185        self.value
186    }
187    /// Returns the labeled asset, if it exists and matches this type.
188    pub fn get_labeled<B: Asset, Q>(&mut self, label: &Q) -> Option<TransformedSubAsset<B>>
189    where
190        CowArc<'static, str>: Borrow<Q>,
191        Q: ?Sized + Hash + Eq,
192    {
193        let labeled = self.labeled_assets.get_mut(label)?;
194        let value = labeled.asset.value.downcast_mut::<B>()?;
195        Some(TransformedSubAsset {
196            value,
197            labeled_assets: &mut labeled.asset.labeled_assets,
198        })
199    }
200    /// Returns the type-erased labeled asset, if it exists and matches this type.
201    pub fn get_erased_labeled<Q>(&self, label: &Q) -> Option<&ErasedLoadedAsset>
202    where
203        CowArc<'static, str>: Borrow<Q>,
204        Q: ?Sized + Hash + Eq,
205    {
206        let labeled = self.labeled_assets.get(label)?;
207        Some(&labeled.asset)
208    }
209    /// Returns the [`UntypedHandle`] of the labeled asset with the provided 'label', if it exists.
210    pub fn get_untyped_handle<Q>(&self, label: &Q) -> Option<UntypedHandle>
211    where
212        CowArc<'static, str>: Borrow<Q>,
213        Q: ?Sized + Hash + Eq,
214    {
215        let labeled = self.labeled_assets.get(label)?;
216        Some(labeled.handle.clone())
217    }
218    /// Returns the [`Handle`] of the labeled asset with the provided 'label', if it exists and is an asset of type `B`
219    pub fn get_handle<Q, B: Asset>(&self, label: &Q) -> Option<Handle<B>>
220    where
221        CowArc<'static, str>: Borrow<Q>,
222        Q: ?Sized + Hash + Eq,
223    {
224        let labeled = self.labeled_assets.get(label)?;
225        if let Ok(handle) = labeled.handle.clone().try_typed::<B>() {
226            return Some(handle);
227        }
228        None
229    }
230    /// Adds `asset` as a labeled sub asset using `label` and `handle`
231    pub fn insert_labeled(
232        &mut self,
233        label: impl Into<CowArc<'static, str>>,
234        handle: impl Into<UntypedHandle>,
235        asset: impl Into<ErasedLoadedAsset>,
236    ) {
237        let labeled = LabeledAsset {
238            asset: asset.into(),
239            handle: handle.into(),
240        };
241        self.labeled_assets.insert(label.into(), labeled);
242    }
243    /// Iterate over all labels for "labeled assets" in the loaded asset
244    pub fn iter_labels(&self) -> impl Iterator<Item = &str> {
245        self.labeled_assets.keys().map(|s| &**s)
246    }
247}
248
249/// An identity [`AssetTransformer`] which infallibly returns the input [`Asset`] on transformation.]
250pub struct IdentityAssetTransformer<A: Asset> {
251    _phantom: PhantomData<fn(A) -> A>,
252}
253
254impl<A: Asset> IdentityAssetTransformer<A> {
255    pub const fn new() -> Self {
256        Self {
257            _phantom: PhantomData,
258        }
259    }
260}
261
262impl<A: Asset> Default for IdentityAssetTransformer<A> {
263    fn default() -> Self {
264        Self::new()
265    }
266}
267
268impl<A: Asset> AssetTransformer for IdentityAssetTransformer<A> {
269    type AssetInput = A;
270    type AssetOutput = A;
271    type Settings = ();
272    type Error = Infallible;
273
274    async fn transform<'a>(
275        &'a self,
276        asset: TransformedAsset<Self::AssetInput>,
277        _settings: &'a Self::Settings,
278    ) -> Result<TransformedAsset<Self::AssetOutput>, Self::Error> {
279        Ok(asset)
280    }
281}