1use crate::io::SliceReader;
2use crate::{
3 io::{
4 AssetReaderError, AssetWriterError, MissingAssetWriterError,
5 MissingProcessedAssetReaderError, MissingProcessedAssetWriterError, Writer,
6 },
7 meta::{AssetAction, AssetMeta, AssetMetaDyn, ProcessDependencyInfo, ProcessedInfo, Settings},
8 processor::AssetProcessor,
9 saver::{AssetSaver, SavedAsset},
10 transformer::{AssetTransformer, TransformedAsset},
11 AssetLoadError, AssetLoader, AssetPath, DeserializeMetaError, ErasedLoadedAsset,
12 MissingAssetLoaderForExtensionError, MissingAssetLoaderForTypeNameError,
13};
14use bevy_utils::{BoxedFuture, ConditionalSendFuture};
15use serde::{Deserialize, Serialize};
16use std::marker::PhantomData;
17use thiserror::Error;
18
19pub trait Process: Send + Sync + Sized + 'static {
25 type Settings: Settings + Default + Serialize + for<'a> Deserialize<'a>;
27 type OutputLoader: AssetLoader;
29 fn process<'a>(
32 &'a self,
33 context: &'a mut ProcessContext,
34 meta: AssetMeta<(), Self>,
35 writer: &'a mut Writer,
36 ) -> impl ConditionalSendFuture<
37 Output = Result<<Self::OutputLoader as AssetLoader>::Settings, ProcessError>,
38 >;
39}
40
41pub struct LoadTransformAndSave<
54 L: AssetLoader,
55 T: AssetTransformer<AssetInput = L::Asset>,
56 S: AssetSaver<Asset = T::AssetOutput>,
57> {
58 transformer: T,
59 saver: S,
60 marker: PhantomData<fn() -> L>,
61}
62
63#[derive(Serialize, Deserialize, Default)]
68pub struct LoadTransformAndSaveSettings<LoaderSettings, TransformerSettings, SaverSettings> {
69 pub loader_settings: LoaderSettings,
71 pub transformer_settings: TransformerSettings,
73 pub saver_settings: SaverSettings,
75}
76
77impl<
78 L: AssetLoader,
79 T: AssetTransformer<AssetInput = L::Asset>,
80 S: AssetSaver<Asset = T::AssetOutput>,
81 > LoadTransformAndSave<L, T, S>
82{
83 pub fn new(transformer: T, saver: S) -> Self {
84 LoadTransformAndSave {
85 transformer,
86 saver,
87 marker: PhantomData,
88 }
89 }
90}
91
92pub struct LoadAndSave<L: AssetLoader, S: AssetSaver<Asset = L::Asset>> {
102 saver: S,
103 marker: PhantomData<fn() -> L>,
104}
105
106impl<L: AssetLoader, S: AssetSaver<Asset = L::Asset>> From<S> for LoadAndSave<L, S> {
107 fn from(value: S) -> Self {
108 LoadAndSave {
109 saver: value,
110 marker: PhantomData,
111 }
112 }
113}
114
115#[derive(Serialize, Deserialize, Default)]
119pub struct LoadAndSaveSettings<LoaderSettings, SaverSettings> {
120 pub loader_settings: LoaderSettings,
122 pub saver_settings: SaverSettings,
124}
125
126#[derive(Error, Debug)]
128pub enum ProcessError {
129 #[error(transparent)]
130 MissingAssetLoaderForExtension(#[from] MissingAssetLoaderForExtensionError),
131 #[error(transparent)]
132 MissingAssetLoaderForTypeName(#[from] MissingAssetLoaderForTypeNameError),
133 #[error("The processor '{0}' does not exist")]
134 MissingProcessor(String),
135 #[error("Encountered an AssetReader error for '{path}': {err}")]
136 AssetReaderError {
137 path: AssetPath<'static>,
138 err: AssetReaderError,
139 },
140 #[error("Encountered an AssetWriter error for '{path}': {err}")]
141 AssetWriterError {
142 path: AssetPath<'static>,
143 err: AssetWriterError,
144 },
145 #[error(transparent)]
146 MissingAssetWriterError(#[from] MissingAssetWriterError),
147 #[error(transparent)]
148 MissingProcessedAssetReaderError(#[from] MissingProcessedAssetReaderError),
149 #[error(transparent)]
150 MissingProcessedAssetWriterError(#[from] MissingProcessedAssetWriterError),
151 #[error("Failed to read asset metadata for {path}: {err}")]
152 ReadAssetMetaError {
153 path: AssetPath<'static>,
154 err: AssetReaderError,
155 },
156 #[error(transparent)]
157 DeserializeMetaError(#[from] DeserializeMetaError),
158 #[error(transparent)]
159 AssetLoadError(#[from] AssetLoadError),
160 #[error("The wrong meta type was passed into a processor. This is probably an internal implementation error.")]
161 WrongMetaType,
162 #[error("Encountered an error while saving the asset: {0}")]
163 AssetSaveError(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
164 #[error("Encountered an error while transforming the asset: {0}")]
165 AssetTransformError(Box<dyn std::error::Error + Send + Sync + 'static>),
166 #[error("Assets without extensions are not supported.")]
167 ExtensionRequired,
168}
169
170impl<Loader, Transformer, Saver> Process for LoadTransformAndSave<Loader, Transformer, Saver>
171where
172 Loader: AssetLoader,
173 Transformer: AssetTransformer<AssetInput = Loader::Asset>,
174 Saver: AssetSaver<Asset = Transformer::AssetOutput>,
175{
176 type Settings =
177 LoadTransformAndSaveSettings<Loader::Settings, Transformer::Settings, Saver::Settings>;
178 type OutputLoader = Saver::OutputLoader;
179
180 async fn process<'a>(
181 &'a self,
182 context: &'a mut ProcessContext<'_>,
183 meta: AssetMeta<(), Self>,
184 writer: &'a mut Writer,
185 ) -> Result<<Self::OutputLoader as AssetLoader>::Settings, ProcessError> {
186 let AssetAction::Process { settings, .. } = meta.asset else {
187 return Err(ProcessError::WrongMetaType);
188 };
189 let loader_meta = AssetMeta::<Loader, ()>::new(AssetAction::Load {
190 loader: std::any::type_name::<Loader>().to_string(),
191 settings: settings.loader_settings,
192 });
193 let pre_transformed_asset = TransformedAsset::<Loader::Asset>::from_loaded(
194 context.load_source_asset(loader_meta).await?,
195 )
196 .unwrap();
197
198 let post_transformed_asset = self
199 .transformer
200 .transform(pre_transformed_asset, &settings.transformer_settings)
201 .await
202 .map_err(|err| ProcessError::AssetTransformError(err.into()))?;
203
204 let saved_asset =
205 SavedAsset::<Transformer::AssetOutput>::from_transformed(&post_transformed_asset);
206
207 let output_settings = self
208 .saver
209 .save(writer, saved_asset, &settings.saver_settings)
210 .await
211 .map_err(|error| ProcessError::AssetSaveError(error.into()))?;
212 Ok(output_settings)
213 }
214}
215
216impl<Loader: AssetLoader, Saver: AssetSaver<Asset = Loader::Asset>> Process
217 for LoadAndSave<Loader, Saver>
218{
219 type Settings = LoadAndSaveSettings<Loader::Settings, Saver::Settings>;
220 type OutputLoader = Saver::OutputLoader;
221
222 async fn process<'a>(
223 &'a self,
224 context: &'a mut ProcessContext<'_>,
225 meta: AssetMeta<(), Self>,
226 writer: &'a mut Writer,
227 ) -> Result<<Self::OutputLoader as AssetLoader>::Settings, ProcessError> {
228 let AssetAction::Process { settings, .. } = meta.asset else {
229 return Err(ProcessError::WrongMetaType);
230 };
231 let loader_meta = AssetMeta::<Loader, ()>::new(AssetAction::Load {
232 loader: std::any::type_name::<Loader>().to_string(),
233 settings: settings.loader_settings,
234 });
235 let loaded_asset = context.load_source_asset(loader_meta).await?;
236 let saved_asset = SavedAsset::<Loader::Asset>::from_loaded(&loaded_asset).unwrap();
237 let output_settings = self
238 .saver
239 .save(writer, saved_asset, &settings.saver_settings)
240 .await
241 .map_err(|error| ProcessError::AssetSaveError(error.into()))?;
242 Ok(output_settings)
243 }
244}
245
246pub trait ErasedProcessor: Send + Sync {
249 fn process<'a>(
251 &'a self,
252 context: &'a mut ProcessContext,
253 meta: Box<dyn AssetMetaDyn>,
254 writer: &'a mut Writer,
255 ) -> BoxedFuture<'a, Result<Box<dyn AssetMetaDyn>, ProcessError>>;
256 fn deserialize_meta(&self, meta: &[u8]) -> Result<Box<dyn AssetMetaDyn>, DeserializeMetaError>;
259 fn default_meta(&self) -> Box<dyn AssetMetaDyn>;
261}
262
263impl<P: Process> ErasedProcessor for P {
264 fn process<'a>(
265 &'a self,
266 context: &'a mut ProcessContext,
267 meta: Box<dyn AssetMetaDyn>,
268 writer: &'a mut Writer,
269 ) -> BoxedFuture<'a, Result<Box<dyn AssetMetaDyn>, ProcessError>> {
270 Box::pin(async move {
271 let meta = meta
272 .downcast::<AssetMeta<(), P>>()
273 .map_err(|_e| ProcessError::WrongMetaType)?;
274 let loader_settings = <P as Process>::process(self, context, *meta, writer).await?;
275 let output_meta: Box<dyn AssetMetaDyn> =
276 Box::new(AssetMeta::<P::OutputLoader, ()>::new(AssetAction::Load {
277 loader: std::any::type_name::<P::OutputLoader>().to_string(),
278 settings: loader_settings,
279 }));
280 Ok(output_meta)
281 })
282 }
283
284 fn deserialize_meta(&self, meta: &[u8]) -> Result<Box<dyn AssetMetaDyn>, DeserializeMetaError> {
285 let meta: AssetMeta<(), P> = ron::de::from_bytes(meta)?;
286 Ok(Box::new(meta))
287 }
288
289 fn default_meta(&self) -> Box<dyn AssetMetaDyn> {
290 Box::new(AssetMeta::<(), P>::new(AssetAction::Process {
291 processor: std::any::type_name::<P>().to_string(),
292 settings: P::Settings::default(),
293 }))
294 }
295}
296
297pub struct ProcessContext<'a> {
300 pub(crate) new_processed_info: &'a mut ProcessedInfo,
310 processor: &'a AssetProcessor,
319 path: &'a AssetPath<'static>,
320 asset_bytes: &'a [u8],
321}
322
323impl<'a> ProcessContext<'a> {
324 pub(crate) fn new(
325 processor: &'a AssetProcessor,
326 path: &'a AssetPath<'static>,
327 asset_bytes: &'a [u8],
328 new_processed_info: &'a mut ProcessedInfo,
329 ) -> Self {
330 Self {
331 processor,
332 path,
333 asset_bytes,
334 new_processed_info,
335 }
336 }
337
338 pub async fn load_source_asset<L: AssetLoader>(
343 &mut self,
344 meta: AssetMeta<L, ()>,
345 ) -> Result<ErasedLoadedAsset, AssetLoadError> {
346 let server = &self.processor.server;
347 let loader_name = std::any::type_name::<L>();
348 let loader = server.get_asset_loader_with_type_name(loader_name).await?;
349 let mut reader = SliceReader::new(self.asset_bytes);
350 let loaded_asset = server
351 .load_with_meta_loader_and_reader(
352 self.path,
353 Box::new(meta),
354 &*loader,
355 &mut reader,
356 false,
357 true,
358 )
359 .await?;
360 for (path, full_hash) in &loaded_asset.loader_dependencies {
361 self.new_processed_info
362 .process_dependencies
363 .push(ProcessDependencyInfo {
364 full_hash: *full_hash,
365 path: path.to_owned(),
366 });
367 }
368 Ok(loaded_asset)
369 }
370
371 #[inline]
373 pub fn path(&self) -> &AssetPath<'static> {
374 self.path
375 }
376
377 #[inline]
379 pub fn asset_bytes(&self) -> &[u8] {
380 self.asset_bytes
381 }
382}