1use crate::{
2 io::{
3 AssetReaderError, AssetWriterError, MissingAssetWriterError,
4 MissingProcessedAssetReaderError, MissingProcessedAssetWriterError, SliceReader, Writer,
5 },
6 meta::{AssetAction, AssetMeta, AssetMetaDyn, ProcessDependencyInfo, ProcessedInfo, Settings},
7 processor::AssetProcessor,
8 saver::{AssetSaver, SavedAsset},
9 transformer::{AssetTransformer, IdentityAssetTransformer, TransformedAsset},
10 AssetLoadError, AssetLoader, AssetPath, DeserializeMetaError, ErasedLoadedAsset,
11 MissingAssetLoaderForExtensionError, MissingAssetLoaderForTypeNameError,
12};
13use bevy_utils::{BoxedFuture, ConditionalSendFuture};
14use core::marker::PhantomData;
15use derive_more::derive::{Display, Error, From};
16use serde::{Deserialize, Serialize};
17
18pub trait Process: Send + Sync + Sized + 'static {
24 type Settings: Settings + Default + Serialize + for<'a> Deserialize<'a>;
26 type OutputLoader: AssetLoader;
28 fn process(
31 &self,
32 context: &mut ProcessContext,
33 meta: AssetMeta<(), Self>,
34 writer: &mut Writer,
35 ) -> impl ConditionalSendFuture<
36 Output = Result<<Self::OutputLoader as AssetLoader>::Settings, ProcessError>,
37 >;
38}
39
40pub struct LoadTransformAndSave<
58 L: AssetLoader,
59 T: AssetTransformer<AssetInput = L::Asset>,
60 S: AssetSaver<Asset = T::AssetOutput>,
61> {
62 transformer: T,
63 saver: S,
64 marker: PhantomData<fn() -> L>,
65}
66
67impl<L: AssetLoader, S: AssetSaver<Asset = L::Asset>> From<S>
68 for LoadTransformAndSave<L, IdentityAssetTransformer<L::Asset>, S>
69{
70 fn from(value: S) -> Self {
71 LoadTransformAndSave {
72 transformer: IdentityAssetTransformer::new(),
73 saver: value,
74 marker: PhantomData,
75 }
76 }
77}
78
79#[derive(Serialize, Deserialize, Default)]
84pub struct LoadTransformAndSaveSettings<LoaderSettings, TransformerSettings, SaverSettings> {
85 pub loader_settings: LoaderSettings,
87 pub transformer_settings: TransformerSettings,
89 pub saver_settings: SaverSettings,
91}
92
93impl<
94 L: AssetLoader,
95 T: AssetTransformer<AssetInput = L::Asset>,
96 S: AssetSaver<Asset = T::AssetOutput>,
97 > LoadTransformAndSave<L, T, S>
98{
99 pub fn new(transformer: T, saver: S) -> Self {
100 LoadTransformAndSave {
101 transformer,
102 saver,
103 marker: PhantomData,
104 }
105 }
106}
107
108#[deprecated = "Use `LoadTransformAndSave<L, IdentityAssetTransformer<<L as AssetLoader>::Asset>, S>` instead"]
118pub type LoadAndSave<L, S> =
119 LoadTransformAndSave<L, IdentityAssetTransformer<<L as AssetLoader>::Asset>, S>;
120
121#[deprecated = "Use `LoadTransformAndSaveSettings<LoaderSettings, (), SaverSettings>` instead"]
125pub type LoadAndSaveSettings<LoaderSettings, SaverSettings> =
126 LoadTransformAndSaveSettings<LoaderSettings, (), SaverSettings>;
127
128#[derive(Error, Display, Debug, From)]
130pub enum ProcessError {
131 MissingAssetLoaderForExtension(MissingAssetLoaderForExtensionError),
132 MissingAssetLoaderForTypeName(MissingAssetLoaderForTypeNameError),
133 #[display("The processor '{_0}' does not exist")]
134 #[error(ignore)]
135 #[from(ignore)]
136 MissingProcessor(String),
137 #[display("Encountered an AssetReader error for '{path}': {err}")]
138 #[from(ignore)]
139 AssetReaderError {
140 path: AssetPath<'static>,
141 err: AssetReaderError,
142 },
143 #[display("Encountered an AssetWriter error for '{path}': {err}")]
144 #[from(ignore)]
145 AssetWriterError {
146 path: AssetPath<'static>,
147 err: AssetWriterError,
148 },
149 MissingAssetWriterError(MissingAssetWriterError),
150 MissingProcessedAssetReaderError(MissingProcessedAssetReaderError),
151 MissingProcessedAssetWriterError(MissingProcessedAssetWriterError),
152 #[display("Failed to read asset metadata for {path}: {err}")]
153 #[from(ignore)]
154 ReadAssetMetaError {
155 path: AssetPath<'static>,
156 err: AssetReaderError,
157 },
158 DeserializeMetaError(DeserializeMetaError),
159 AssetLoadError(AssetLoadError),
160 #[display("The wrong meta type was passed into a processor. This is probably an internal implementation error.")]
161 WrongMetaType,
162 #[display("Encountered an error while saving the asset: {_0}")]
163 #[from(ignore)]
164 AssetSaveError(Box<dyn core::error::Error + Send + Sync + 'static>),
165 #[display("Encountered an error while transforming the asset: {_0}")]
166 #[from(ignore)]
167 AssetTransformError(Box<dyn core::error::Error + Send + Sync + 'static>),
168 #[display("Assets without extensions are not supported.")]
169 ExtensionRequired,
170}
171
172impl<Loader, Transformer, Saver> Process for LoadTransformAndSave<Loader, Transformer, Saver>
173where
174 Loader: AssetLoader,
175 Transformer: AssetTransformer<AssetInput = Loader::Asset>,
176 Saver: AssetSaver<Asset = Transformer::AssetOutput>,
177{
178 type Settings =
179 LoadTransformAndSaveSettings<Loader::Settings, Transformer::Settings, Saver::Settings>;
180 type OutputLoader = Saver::OutputLoader;
181
182 async fn process(
183 &self,
184 context: &mut ProcessContext<'_>,
185 meta: AssetMeta<(), Self>,
186 writer: &mut Writer,
187 ) -> Result<<Self::OutputLoader as AssetLoader>::Settings, ProcessError> {
188 let AssetAction::Process { settings, .. } = meta.asset else {
189 return Err(ProcessError::WrongMetaType);
190 };
191 let loader_meta = AssetMeta::<Loader, ()>::new(AssetAction::Load {
192 loader: core::any::type_name::<Loader>().to_string(),
193 settings: settings.loader_settings,
194 });
195 let pre_transformed_asset = TransformedAsset::<Loader::Asset>::from_loaded(
196 context.load_source_asset(loader_meta).await?,
197 )
198 .unwrap();
199
200 let post_transformed_asset = self
201 .transformer
202 .transform(pre_transformed_asset, &settings.transformer_settings)
203 .await
204 .map_err(|err| ProcessError::AssetTransformError(err.into()))?;
205
206 let saved_asset =
207 SavedAsset::<Transformer::AssetOutput>::from_transformed(&post_transformed_asset);
208
209 let output_settings = self
210 .saver
211 .save(writer, saved_asset, &settings.saver_settings)
212 .await
213 .map_err(|error| ProcessError::AssetSaveError(error.into()))?;
214 Ok(output_settings)
215 }
216}
217
218pub trait ErasedProcessor: Send + Sync {
221 fn process<'a>(
223 &'a self,
224 context: &'a mut ProcessContext,
225 meta: Box<dyn AssetMetaDyn>,
226 writer: &'a mut Writer,
227 ) -> BoxedFuture<'a, Result<Box<dyn AssetMetaDyn>, ProcessError>>;
228 fn deserialize_meta(&self, meta: &[u8]) -> Result<Box<dyn AssetMetaDyn>, DeserializeMetaError>;
231 fn default_meta(&self) -> Box<dyn AssetMetaDyn>;
233}
234
235impl<P: Process> ErasedProcessor for P {
236 fn process<'a>(
237 &'a self,
238 context: &'a mut ProcessContext,
239 meta: Box<dyn AssetMetaDyn>,
240 writer: &'a mut Writer,
241 ) -> BoxedFuture<'a, Result<Box<dyn AssetMetaDyn>, ProcessError>> {
242 Box::pin(async move {
243 let meta = meta
244 .downcast::<AssetMeta<(), P>>()
245 .map_err(|_e| ProcessError::WrongMetaType)?;
246 let loader_settings = <P as Process>::process(self, context, *meta, writer).await?;
247 let output_meta: Box<dyn AssetMetaDyn> =
248 Box::new(AssetMeta::<P::OutputLoader, ()>::new(AssetAction::Load {
249 loader: core::any::type_name::<P::OutputLoader>().to_string(),
250 settings: loader_settings,
251 }));
252 Ok(output_meta)
253 })
254 }
255
256 fn deserialize_meta(&self, meta: &[u8]) -> Result<Box<dyn AssetMetaDyn>, DeserializeMetaError> {
257 let meta: AssetMeta<(), P> = ron::de::from_bytes(meta)?;
258 Ok(Box::new(meta))
259 }
260
261 fn default_meta(&self) -> Box<dyn AssetMetaDyn> {
262 Box::new(AssetMeta::<(), P>::new(AssetAction::Process {
263 processor: core::any::type_name::<P>().to_string(),
264 settings: P::Settings::default(),
265 }))
266 }
267}
268
269pub struct ProcessContext<'a> {
272 pub(crate) new_processed_info: &'a mut ProcessedInfo,
282 processor: &'a AssetProcessor,
291 path: &'a AssetPath<'static>,
292 asset_bytes: &'a [u8],
293}
294
295impl<'a> ProcessContext<'a> {
296 pub(crate) fn new(
297 processor: &'a AssetProcessor,
298 path: &'a AssetPath<'static>,
299 asset_bytes: &'a [u8],
300 new_processed_info: &'a mut ProcessedInfo,
301 ) -> Self {
302 Self {
303 processor,
304 path,
305 asset_bytes,
306 new_processed_info,
307 }
308 }
309
310 pub async fn load_source_asset<L: AssetLoader>(
315 &mut self,
316 meta: AssetMeta<L, ()>,
317 ) -> Result<ErasedLoadedAsset, AssetLoadError> {
318 let server = &self.processor.server;
319 let loader_name = core::any::type_name::<L>();
320 let loader = server.get_asset_loader_with_type_name(loader_name).await?;
321 let mut reader = SliceReader::new(self.asset_bytes);
322 let loaded_asset = server
323 .load_with_meta_loader_and_reader(
324 self.path,
325 Box::new(meta),
326 &*loader,
327 &mut reader,
328 false,
329 true,
330 )
331 .await?;
332 for (path, full_hash) in &loaded_asset.loader_dependencies {
333 self.new_processed_info
334 .process_dependencies
335 .push(ProcessDependencyInfo {
336 full_hash: *full_hash,
337 path: path.to_owned(),
338 });
339 }
340 Ok(loaded_asset)
341 }
342
343 #[inline]
345 pub fn path(&self) -> &AssetPath<'static> {
346 self.path
347 }
348
349 #[inline]
351 pub fn asset_bytes(&self) -> &[u8] {
352 self.asset_bytes
353 }
354}