bevy_asset/
transformer.rs

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