1mod log;
41mod process;
42
43use async_lock::RwLockReadGuardArc;
44pub use log::*;
45pub use process::*;
46
47use crate::{
48 io::{
49 AssetReaderError, AssetSource, AssetSourceBuilders, AssetSourceEvent, AssetSourceId,
50 AssetSources, AssetWriterError, ErasedAssetReader, MissingAssetSourceError,
51 },
52 meta::{
53 get_asset_hash, get_full_asset_hash, AssetAction, AssetActionMinimal, AssetHash, AssetMeta,
54 AssetMetaDyn, AssetMetaMinimal, ProcessedInfo, ProcessedInfoMinimal,
55 },
56 AssetLoadError, AssetMetaCheck, AssetPath, AssetServer, AssetServerMode, DeserializeMetaError,
57 MissingAssetLoaderForExtensionError, UnapprovedPathMode, WriteDefaultMetaError,
58};
59use alloc::{borrow::ToOwned, boxed::Box, string::String, sync::Arc, vec, vec::Vec};
60use bevy_ecs::prelude::*;
61use bevy_platform::{
62 collections::{hash_map::Entry, HashMap, HashSet},
63 sync::{PoisonError, RwLock},
64};
65use bevy_tasks::IoTaskPool;
66use futures_io::ErrorKind;
67use futures_lite::{AsyncWriteExt, StreamExt};
68use futures_util::{select_biased, FutureExt};
69use std::{
70 path::{Path, PathBuf},
71 sync::Mutex,
72};
73use thiserror::Error;
74use tracing::{debug, error, trace, warn};
75
76#[cfg(feature = "trace")]
77use {
78 alloc::string::ToString,
79 tracing::{info_span, instrument::Instrument},
80};
81
82#[derive(Resource, Clone)]
98pub struct AssetProcessor {
99 server: AssetServer,
100 pub(crate) data: Arc<AssetProcessorData>,
101}
102
103pub struct AssetProcessorData {
105 pub(crate) processing_state: Arc<ProcessingState>,
107 log_factory: Mutex<Option<Box<dyn ProcessorTransactionLogFactory>>>,
113 log: async_lock::RwLock<Option<Box<dyn ProcessorTransactionLog>>>,
114 processors: RwLock<Processors>,
116 sources: Arc<AssetSources>,
117}
118
119pub(crate) struct ProcessingState {
121 state: async_lock::RwLock<ProcessorState>,
123 initialized_sender: async_broadcast::Sender<()>,
125 initialized_receiver: async_broadcast::Receiver<()>,
126 finished_sender: async_broadcast::Sender<()>,
128 finished_receiver: async_broadcast::Receiver<()>,
129 asset_infos: async_lock::RwLock<ProcessorAssetInfos>,
131}
132
133#[derive(Default)]
134struct Processors {
135 type_path_to_processor: HashMap<&'static str, Arc<dyn ErasedProcessor>>,
137 short_type_path_to_processor: HashMap<&'static str, ShortTypeProcessorEntry>,
139 file_extension_to_default_processor: HashMap<Box<str>, &'static str>,
142}
143
144enum ShortTypeProcessorEntry {
145 Unique {
147 type_path: &'static str,
149 processor: Arc<dyn ErasedProcessor>,
151 },
152 Ambiguous(Vec<&'static str>),
156}
157
158impl AssetProcessor {
159 pub fn new(
161 sources: &mut AssetSourceBuilders,
162 watch_processed: bool,
163 ) -> (Self, Arc<AssetSources>) {
164 let state = Arc::new(ProcessingState::new());
165 let mut sources = sources.build_sources(true, watch_processed);
166 sources.gate_on_processor(state.clone());
167 let sources = Arc::new(sources);
168
169 let data = Arc::new(AssetProcessorData::new(sources.clone(), state));
170 let server = AssetServer::new_with_meta_check(
172 sources.clone(),
173 AssetServerMode::Processed,
174 AssetMetaCheck::Always,
175 false,
176 UnapprovedPathMode::default(),
177 );
178 (Self { server, data }, sources)
179 }
180
181 pub fn data(&self) -> &Arc<AssetProcessorData> {
183 &self.data
184 }
185
186 pub fn server(&self) -> &AssetServer {
189 &self.server
190 }
191
192 pub async fn get_state(&self) -> ProcessorState {
194 self.data.processing_state.get_state().await
195 }
196
197 #[inline]
199 pub fn get_source<'a>(
200 &self,
201 id: impl Into<AssetSourceId<'a>>,
202 ) -> Result<&AssetSource, MissingAssetSourceError> {
203 self.data.sources.get(id.into())
204 }
205
206 #[inline]
208 pub fn sources(&self) -> &AssetSources {
209 &self.data.sources
210 }
211
212 async fn log_unrecoverable(&self) {
215 let mut log = self.data.log.write().await;
216 let log = log.as_mut().unwrap();
217 log.unrecoverable()
218 .await
219 .map_err(|error| WriteLogError {
220 log_entry: LogEntry::UnrecoverableError,
221 error,
222 })
223 .unwrap();
224 }
225
226 async fn log_begin_processing(&self, path: &AssetPath<'_>) {
229 let mut log = self.data.log.write().await;
230 let log = log.as_mut().unwrap();
231 log.begin_processing(path)
232 .await
233 .map_err(|error| WriteLogError {
234 log_entry: LogEntry::BeginProcessing(path.clone_owned()),
235 error,
236 })
237 .unwrap();
238 }
239
240 async fn log_end_processing(&self, path: &AssetPath<'_>) {
242 let mut log = self.data.log.write().await;
243 let log = log.as_mut().unwrap();
244 log.end_processing(path)
245 .await
246 .map_err(|error| WriteLogError {
247 log_entry: LogEntry::EndProcessing(path.clone_owned()),
248 error,
249 })
250 .unwrap();
251 }
252
253 pub fn start(processor: Res<Self>) {
255 let processor = processor.clone();
256 IoTaskPool::get()
257 .spawn(async move {
258 let start_time = std::time::Instant::now();
259 debug!("Processing Assets");
260
261 processor.initialize().await.unwrap();
262
263 let (new_task_sender, new_task_receiver) = async_channel::unbounded();
264 processor
265 .queue_initial_processing_tasks(&new_task_sender)
266 .await;
267
268 {
271 let processor = processor.clone();
272 let new_task_sender = new_task_sender.clone();
273 IoTaskPool::get()
274 .spawn(async move {
275 processor
276 .execute_processing_tasks(new_task_sender, new_task_receiver)
277 .await;
278 })
279 .detach();
280 }
281
282 processor.data.wait_until_finished().await;
283
284 let end_time = std::time::Instant::now();
285 debug!("Processing finished in {:?}", end_time - start_time);
286
287 debug!("Listening for changes to source assets");
288 processor.spawn_source_change_event_listeners(&new_task_sender);
289 })
290 .detach();
291 }
292
293 async fn queue_initial_processing_tasks(
295 &self,
296 sender: &async_channel::Sender<(AssetSourceId<'static>, PathBuf)>,
297 ) {
298 for source in self.sources().iter_processed() {
299 self.queue_processing_tasks_for_folder(source, PathBuf::from(""), sender)
300 .await
301 .unwrap();
302 }
303 }
304
305 fn spawn_source_change_event_listeners(
308 &self,
309 sender: &async_channel::Sender<(AssetSourceId<'static>, PathBuf)>,
310 ) {
311 for source in self.data.sources.iter_processed() {
312 let Some(receiver) = source.event_receiver().cloned() else {
313 continue;
314 };
315 let source_id = source.id();
316 let processor = self.clone();
317 let sender = sender.clone();
318 IoTaskPool::get()
319 .spawn(async move {
320 while let Ok(event) = receiver.recv().await {
321 let Ok(source) = processor.get_source(source_id.clone()) else {
322 return;
323 };
324 processor
325 .handle_asset_source_event(source, event, &sender)
326 .await;
327 }
328 })
329 .detach();
330 }
331 }
332
333 async fn execute_processing_tasks(
340 &self,
341 new_task_sender: async_channel::Sender<(AssetSourceId<'static>, PathBuf)>,
342 new_task_receiver: async_channel::Receiver<(AssetSourceId<'static>, PathBuf)>,
343 ) {
344 let new_task_sender = {
348 let weak_sender = new_task_sender.downgrade();
349 drop(new_task_sender);
350 weak_sender
351 };
352
353 if new_task_receiver.is_empty() {
357 self.data
358 .processing_state
359 .set_state(ProcessorState::Finished)
360 .await;
361 }
362 enum ProcessorTaskEvent {
363 Start(AssetSourceId<'static>, PathBuf),
364 Finished,
365 }
366 let (task_finished_sender, task_finished_receiver) = async_channel::unbounded::<()>();
367
368 let mut pending_tasks = 0;
369 while let Ok(event) = {
370 select_biased! {
374 result = new_task_receiver.recv().fuse() => {
375 result.map(|(source_id, path)| ProcessorTaskEvent::Start(source_id, path))
376 },
377 result = task_finished_receiver.recv().fuse() => {
378 result.map(|()| ProcessorTaskEvent::Finished)
379 }
380 }
381 } {
382 match event {
383 ProcessorTaskEvent::Start(source_id, path) => {
384 let Some(new_task_sender) = new_task_sender.upgrade() else {
385 continue;
392 };
393 let processor = self.clone();
394 let task_finished_sender = task_finished_sender.clone();
395 pending_tasks += 1;
396 IoTaskPool::get()
397 .spawn(async move {
398 let Ok(source) = processor.get_source(source_id) else {
399 return;
400 };
401 processor.process_asset(source, path, new_task_sender).await;
402 let _ = task_finished_sender.send(()).await;
404 })
405 .detach();
406 self.data
407 .processing_state
408 .set_state(ProcessorState::Processing)
409 .await;
410 }
411 ProcessorTaskEvent::Finished => {
412 pending_tasks -= 1;
413 if pending_tasks == 0 {
414 self.server.write_infos().consume_handle_drop_events();
416 self.data
417 .processing_state
418 .set_state(ProcessorState::Finished)
419 .await;
420 }
421 }
422 }
423 }
424 }
425
426 pub async fn write_default_meta_file_for_path(
434 &self,
435 path: impl Into<AssetPath<'_>>,
436 ) -> Result<(), WriteDefaultMetaError> {
437 let path = path.into();
438 let Some(processor) = path
439 .get_full_extension()
440 .and_then(|extension| self.get_default_processor(extension))
441 else {
442 return self
443 .server
444 .write_default_loader_meta_file_for_path(path)
445 .await;
446 };
447
448 let short_type_path = processor.short_type_path();
449 let processor_path_kind = if self.get_processor(short_type_path).is_ok() {
452 MetaTypePathKind::Short
453 } else {
454 MetaTypePathKind::Long
455 };
456
457 let meta = processor.default_meta(processor_path_kind);
458 let serialized_meta = meta.serialize();
459
460 let source = self.get_source(path.source())?;
461
462 let reader = source.reader();
466 match reader.read_meta_bytes(path.path()).await {
467 Ok(_) => return Err(WriteDefaultMetaError::MetaAlreadyExists),
468 Err(AssetReaderError::NotFound(_)) => {
469 }
471 Err(AssetReaderError::Io(err)) => {
472 return Err(WriteDefaultMetaError::IoErrorFromExistingMetaCheck(err))
473 }
474 Err(AssetReaderError::HttpError(err)) => {
475 return Err(WriteDefaultMetaError::HttpErrorFromExistingMetaCheck(err))
476 }
477 }
478
479 let writer = source.writer()?;
480 writer
481 .write_meta_bytes(path.path(), &serialized_meta)
482 .await?;
483
484 Ok(())
485 }
486
487 async fn handle_asset_source_event(
488 &self,
489 source: &AssetSource,
490 event: AssetSourceEvent,
491 new_task_sender: &async_channel::Sender<(AssetSourceId<'static>, PathBuf)>,
492 ) {
493 trace!("{event:?}");
494 match event {
495 AssetSourceEvent::AddedAsset(path)
496 | AssetSourceEvent::AddedMeta(path)
497 | AssetSourceEvent::ModifiedAsset(path)
498 | AssetSourceEvent::ModifiedMeta(path) => {
499 let _ = new_task_sender.send((source.id(), path)).await;
500 }
501 AssetSourceEvent::RemovedAsset(path) => {
502 self.handle_removed_asset(source, path).await;
503 }
504 AssetSourceEvent::RemovedMeta(path) => {
505 self.handle_removed_meta(source, path, new_task_sender)
506 .await;
507 }
508 AssetSourceEvent::AddedFolder(path) => {
509 self.handle_added_folder(source, path, new_task_sender)
510 .await;
511 }
512 AssetSourceEvent::RemovedFolder(path) => {
516 self.handle_removed_folder(source, &path).await;
517 }
518 AssetSourceEvent::RenamedAsset { old, new } => {
519 if old == new {
522 let _ = new_task_sender.send((source.id(), new)).await;
523 } else {
524 self.handle_renamed_asset(source, old, new, new_task_sender)
525 .await;
526 }
527 }
528 AssetSourceEvent::RenamedMeta { old, new } => {
529 if old == new {
532 let _ = new_task_sender.send((source.id(), new)).await;
533 } else {
534 debug!("Meta renamed from {old:?} to {new:?}");
535 let _ = new_task_sender.send((source.id(), old)).await;
538 let _ = new_task_sender.send((source.id(), new)).await;
539 }
540 }
541 AssetSourceEvent::RenamedFolder { old, new } => {
542 if old == new {
545 self.handle_added_folder(source, new, new_task_sender).await;
546 } else {
547 self.handle_removed_folder(source, &old).await;
550 self.handle_added_folder(source, new, new_task_sender).await;
551 }
552 }
553 AssetSourceEvent::RemovedUnknown { path, is_meta } => {
554 let processed_reader = source.ungated_processed_reader().unwrap();
555 match processed_reader.is_directory(&path).await {
556 Ok(is_directory) => {
557 if is_directory {
558 self.handle_removed_folder(source, &path).await;
559 } else if is_meta {
560 self.handle_removed_meta(source, path, new_task_sender)
561 .await;
562 } else {
563 self.handle_removed_asset(source, path).await;
564 }
565 }
566 Err(err) => {
567 match err {
568 AssetReaderError::NotFound(_) => {
569 }
571 AssetReaderError::Io(err) => {
572 error!(
573 "Path '{}' was removed, but the destination reader could not determine if it \
574 was a folder or a file due to the following error: {err}",
575 AssetPath::from_path(&path).with_source(source.id())
576 );
577 }
578 AssetReaderError::HttpError(status) => {
579 error!(
580 "Path '{}' was removed, but the destination reader could not determine if it \
581 was a folder or a file due to receiving an unexpected HTTP Status {status}",
582 AssetPath::from_path(&path).with_source(source.id())
583 );
584 }
585 }
586 }
587 }
588 }
589 }
590 }
591
592 async fn handle_added_folder(
593 &self,
594 source: &AssetSource,
595 path: PathBuf,
596 new_task_sender: &async_channel::Sender<(AssetSourceId<'static>, PathBuf)>,
597 ) {
598 debug!(
599 "Folder {} was added. Attempting to re-process",
600 AssetPath::from_path(&path).with_source(source.id())
601 );
602 self.queue_processing_tasks_for_folder(source, path, new_task_sender)
603 .await
604 .unwrap();
605 }
606
607 async fn handle_removed_meta(
609 &self,
610 source: &AssetSource,
611 path: PathBuf,
612 new_task_sender: &async_channel::Sender<(AssetSourceId<'static>, PathBuf)>,
613 ) {
614 debug!(
619 "Meta for asset {} was removed. Attempting to re-process",
620 AssetPath::from_path(&path).with_source(source.id())
621 );
622 let _ = new_task_sender.send((source.id(), path)).await;
623 }
624
625 async fn handle_removed_folder(&self, source: &AssetSource, path: &Path) {
627 debug!(
628 "Removing folder {} because source was removed",
629 path.display()
630 );
631 let processed_reader = source.ungated_processed_reader().unwrap();
632 match processed_reader.read_directory(path).await {
633 Ok(mut path_stream) => {
634 while let Some(child_path) = path_stream.next().await {
635 self.handle_removed_asset(source, child_path).await;
636 }
637 }
638 Err(err) => match err {
639 AssetReaderError::NotFound(_err) => {
640 }
642 AssetReaderError::HttpError(status) => {
643 self.log_unrecoverable().await;
644 error!(
645 "Unrecoverable Error: Failed to read the processed assets at {path:?} in order to remove assets that no longer exist \
646 in the source directory. Restart the asset processor to fully reprocess assets. HTTP Status Code {status}"
647 );
648 }
649 AssetReaderError::Io(err) => {
650 self.log_unrecoverable().await;
651 error!(
652 "Unrecoverable Error: Failed to read the processed assets at {path:?} in order to remove assets that no longer exist \
653 in the source directory. Restart the asset processor to fully reprocess assets. Error: {err}"
654 );
655 }
656 },
657 }
658 let processed_writer = source.processed_writer().unwrap();
659 if let Err(err) = processed_writer.remove_directory(path).await {
660 match err {
661 AssetWriterError::Io(err) => {
662 if err.kind() != ErrorKind::NotFound {
665 let asset_path = AssetPath::from_path(path).with_source(source.id());
666 error!("Failed to remove destination folder that no longer exists in {asset_path}: {err}");
667 }
668 }
669 }
670 }
671 }
672
673 async fn handle_removed_asset(&self, source: &AssetSource, path: PathBuf) {
676 let asset_path = AssetPath::from(path).with_source(source.id());
677 debug!("Removing processed {asset_path} because source was removed");
678 let lock = {
679 let mut infos = self.data.processing_state.asset_infos.write().await;
681 infos.remove(&asset_path).await
682 };
683 let Some(lock) = lock else {
684 return;
685 };
686
687 let _write_lock = lock.write();
690 self.remove_processed_asset_and_meta(source, asset_path.path())
691 .await;
692 }
693
694 async fn handle_renamed_asset(
697 &self,
698 source: &AssetSource,
699 old: PathBuf,
700 new: PathBuf,
701 new_task_sender: &async_channel::Sender<(AssetSourceId<'static>, PathBuf)>,
702 ) {
703 let old = AssetPath::from(old).with_source(source.id());
704 let new = AssetPath::from(new).with_source(source.id());
705 let processed_writer = source.processed_writer().unwrap();
706 let result = {
707 let mut infos = self.data.processing_state.asset_infos.write().await;
709 infos.rename(&old, &new, new_task_sender).await
710 };
711 let Some((old_lock, new_lock)) = result else {
712 return;
713 };
714 let _old_write_lock = old_lock.write();
717 let _new_write_lock = new_lock.write();
718 processed_writer
719 .rename(old.path(), new.path())
720 .await
721 .unwrap();
722 processed_writer
723 .rename_meta(old.path(), new.path())
724 .await
725 .unwrap();
726 }
727
728 async fn queue_processing_tasks_for_folder(
729 &self,
730 source: &AssetSource,
731 path: PathBuf,
732 new_task_sender: &async_channel::Sender<(AssetSourceId<'static>, PathBuf)>,
733 ) -> Result<(), AssetReaderError> {
734 if source.reader().is_directory(&path).await? {
735 let mut path_stream = source.reader().read_directory(&path).await?;
736 while let Some(path) = path_stream.next().await {
737 Box::pin(self.queue_processing_tasks_for_folder(source, path, new_task_sender))
738 .await?;
739 }
740 } else {
741 let _ = new_task_sender.send((source.id(), path)).await;
742 }
743 Ok(())
744 }
745
746 pub fn register_processor<P: Process>(&self, processor: P) {
748 let mut processors = self
749 .data
750 .processors
751 .write()
752 .unwrap_or_else(PoisonError::into_inner);
753 let processor = Arc::new(processor);
754 processors
755 .type_path_to_processor
756 .insert(P::type_path(), processor.clone());
757 match processors
758 .short_type_path_to_processor
759 .entry(P::short_type_path())
760 {
761 Entry::Vacant(entry) => {
762 entry.insert(ShortTypeProcessorEntry::Unique {
763 type_path: P::type_path(),
764 processor,
765 });
766 }
767 Entry::Occupied(mut entry) => match entry.get_mut() {
768 ShortTypeProcessorEntry::Unique { type_path, .. } => {
769 let type_path = *type_path;
770 *entry.get_mut() =
771 ShortTypeProcessorEntry::Ambiguous(vec![type_path, P::type_path()]);
772 }
773 ShortTypeProcessorEntry::Ambiguous(type_paths) => {
774 type_paths.push(P::type_path());
775 }
776 },
777 }
778 }
779
780 pub fn set_default_processor<P: Process>(&self, extension: &str) {
782 let mut processors = self
783 .data
784 .processors
785 .write()
786 .unwrap_or_else(PoisonError::into_inner);
787 processors
788 .file_extension_to_default_processor
789 .insert(extension.into(), P::type_path());
790 }
791
792 pub fn get_default_processor(&self, extension: &str) -> Option<Arc<dyn ErasedProcessor>> {
794 let processors = self
795 .data
796 .processors
797 .read()
798 .unwrap_or_else(PoisonError::into_inner);
799 let key = processors
800 .file_extension_to_default_processor
801 .get(extension)?;
802 processors.type_path_to_processor.get(key).cloned()
803 }
804
805 pub fn get_processor(
807 &self,
808 processor_type_name: &str,
809 ) -> Result<Arc<dyn ErasedProcessor>, GetProcessorError> {
810 let processors = self
811 .data
812 .processors
813 .read()
814 .unwrap_or_else(PoisonError::into_inner);
815 if let Some(short_type_processor) = processors
816 .short_type_path_to_processor
817 .get(processor_type_name)
818 {
819 return match short_type_processor {
820 ShortTypeProcessorEntry::Unique { processor, .. } => Ok(processor.clone()),
821 ShortTypeProcessorEntry::Ambiguous(examples) => Err(GetProcessorError::Ambiguous {
822 processor_short_name: processor_type_name.to_owned(),
823 ambiguous_processor_names: examples.clone(),
824 }),
825 };
826 }
827 processors
828 .type_path_to_processor
829 .get(processor_type_name)
830 .cloned()
831 .ok_or_else(|| GetProcessorError::Missing(processor_type_name.to_owned()))
832 }
833
834 async fn initialize(&self) -> Result<(), InitializeError> {
839 self.validate_transaction_log_and_recover().await;
840 let mut asset_infos = self.data.processing_state.asset_infos.write().await;
841
842 async fn get_asset_paths(
845 reader: &dyn ErasedAssetReader,
846 path: PathBuf,
847 paths: &mut Vec<PathBuf>,
848 mut empty_dirs: Option<&mut Vec<PathBuf>>,
849 ) -> Result<bool, AssetReaderError> {
850 if reader.is_directory(&path).await? {
851 let mut path_stream = reader.read_directory(&path).await?;
852 let mut contains_files = false;
853
854 while let Some(child_path) = path_stream.next().await {
855 contains_files |= Box::pin(get_asset_paths(
856 reader,
857 child_path,
858 paths,
859 empty_dirs.as_deref_mut(),
860 ))
861 .await?;
862 }
863 if !contains_files
866 && path.parent().is_some()
867 && let Some(empty_dirs) = empty_dirs
868 {
869 empty_dirs.push(path);
870 }
871 Ok(contains_files)
872 } else {
873 paths.push(path);
874 Ok(true)
875 }
876 }
877
878 for source in self.sources().iter_processed() {
879 let Some(processed_reader) = source.ungated_processed_reader() else {
880 continue;
881 };
882 let Ok(processed_writer) = source.processed_writer() else {
883 continue;
884 };
885 let mut unprocessed_paths = Vec::new();
886 get_asset_paths(
887 source.reader(),
888 PathBuf::from(""),
889 &mut unprocessed_paths,
890 None,
891 )
892 .await
893 .map_err(InitializeError::FailedToReadSourcePaths)?;
894
895 let mut processed_paths = Vec::new();
896 let mut empty_dirs = Vec::new();
897 get_asset_paths(
898 processed_reader,
899 PathBuf::from(""),
900 &mut processed_paths,
901 Some(&mut empty_dirs),
902 )
903 .await
904 .map_err(InitializeError::FailedToReadDestinationPaths)?;
905
906 for empty_dir in empty_dirs {
910 let _ = processed_writer.remove_empty_directory(&empty_dir).await;
912 }
913
914 for path in unprocessed_paths {
915 asset_infos.get_or_insert(AssetPath::from(path).with_source(source.id()));
916 }
917
918 for path in processed_paths {
919 let mut dependencies = Vec::new();
920 let asset_path = AssetPath::from(path).with_source(source.id());
921 if let Some(info) = asset_infos.get_mut(&asset_path) {
922 match processed_reader.read_meta_bytes(asset_path.path()).await {
923 Ok(meta_bytes) => {
924 match ron::de::from_bytes::<ProcessedInfoMinimal>(&meta_bytes) {
925 Ok(minimal) => {
926 trace!(
927 "Populated processed info for asset {asset_path} {:?}",
928 minimal.processed_info
929 );
930
931 if let Some(processed_info) = &minimal.processed_info {
932 for process_dependency_info in
933 &processed_info.process_dependencies
934 {
935 dependencies.push(process_dependency_info.path.clone());
936 }
937 }
938 info.processed_info = minimal.processed_info;
939 }
940 Err(err) => {
941 trace!("Removing processed data for {asset_path} because meta could not be parsed: {err}");
942 self.remove_processed_asset_and_meta(source, asset_path.path())
943 .await;
944 }
945 }
946 }
947 Err(err) => {
948 trace!("Removing processed data for {asset_path} because meta failed to load: {err}");
949 self.remove_processed_asset_and_meta(source, asset_path.path())
950 .await;
951 }
952 }
953 } else {
954 trace!("Removing processed data for non-existent asset {asset_path}");
955 self.remove_processed_asset_and_meta(source, asset_path.path())
956 .await;
957 }
958
959 for dependency in dependencies {
960 asset_infos.add_dependent(&dependency, asset_path.clone());
961 }
962 }
963 }
964
965 self.data
966 .processing_state
967 .set_state(ProcessorState::Processing)
968 .await;
969
970 Ok(())
971 }
972
973 async fn remove_processed_asset_and_meta(&self, source: &AssetSource, path: &Path) {
976 if let Err(err) = source.processed_writer().unwrap().remove(path).await {
977 warn!("Failed to remove non-existent asset {path:?}: {err}");
978 }
979
980 if let Err(err) = source.processed_writer().unwrap().remove_meta(path).await {
981 warn!("Failed to remove non-existent meta {path:?}: {err}");
982 }
983
984 self.clean_empty_processed_ancestor_folders(source, path)
985 .await;
986 }
987
988 async fn clean_empty_processed_ancestor_folders(&self, source: &AssetSource, mut path: &Path) {
989 if path.is_absolute() {
991 error!("Attempted to clean up ancestor folders of an absolute path. This is unsafe so the operation was skipped.");
992 return;
993 }
994 while let Some(parent) = path.parent() {
995 path = parent;
996 if parent == Path::new("") {
997 break;
998 }
999 if source
1000 .processed_writer()
1001 .unwrap()
1002 .remove_empty_directory(parent)
1003 .await
1004 .is_err()
1005 {
1006 break;
1008 }
1009 }
1010 }
1011
1012 async fn process_asset(
1020 &self,
1021 source: &AssetSource,
1022 path: PathBuf,
1023 processor_task_event: async_channel::Sender<(AssetSourceId<'static>, PathBuf)>,
1024 ) {
1025 let asset_path = AssetPath::from(path).with_source(source.id());
1026 let result = self.process_asset_internal(source, &asset_path).await;
1027 let mut infos = self.data.processing_state.asset_infos.write().await;
1028 infos
1029 .finish_processing(asset_path, result, processor_task_event)
1030 .await;
1031 }
1032
1033 async fn process_asset_internal(
1034 &self,
1035 source: &AssetSource,
1036 asset_path: &AssetPath<'static>,
1037 ) -> Result<ProcessResult, ProcessError> {
1038 debug!("Processing {}", asset_path);
1040 let server = &self.server;
1041 let path = asset_path.path();
1042 let reader = source.reader();
1043
1044 let reader_err = |err| ProcessError::AssetReaderError {
1045 path: asset_path.clone(),
1046 err,
1047 };
1048 let writer_err = |err| ProcessError::AssetWriterError {
1049 path: asset_path.clone(),
1050 err,
1051 };
1052
1053 let (mut source_meta, meta_bytes, processor) = match reader.read_meta_bytes(path).await {
1054 Ok(meta_bytes) => {
1055 let minimal: AssetMetaMinimal = ron::de::from_bytes(&meta_bytes).map_err(|e| {
1056 ProcessError::DeserializeMetaError(DeserializeMetaError::DeserializeMinimal(e))
1057 })?;
1058 let (meta, processor) = match minimal.asset {
1059 AssetActionMinimal::Load { loader } => {
1060 let loader = server.get_asset_loader_with_type_name(&loader).await?;
1061 let meta = loader.deserialize_meta(&meta_bytes)?;
1062 (meta, None)
1063 }
1064 AssetActionMinimal::Process { processor } => {
1065 let processor = self.get_processor(&processor)?;
1066 let meta = processor.deserialize_meta(&meta_bytes)?;
1067 (meta, Some(processor))
1068 }
1069 AssetActionMinimal::Ignore => {
1070 return Ok(ProcessResult::Ignored);
1071 }
1072 };
1073 (meta, meta_bytes, processor)
1074 }
1075 Err(AssetReaderError::NotFound(_path)) => {
1076 let (meta, processor) = if let Some(processor) = asset_path
1077 .get_full_extension()
1078 .and_then(|ext| self.get_default_processor(ext))
1079 {
1080 let meta = processor.default_meta(MetaTypePathKind::Long);
1084 (meta, Some(processor))
1085 } else {
1086 match server.get_path_asset_loader(asset_path.clone()).await {
1087 Ok(loader) => (loader.default_meta(), None),
1088 Err(MissingAssetLoaderForExtensionError { .. }) => {
1089 let meta: Box<dyn AssetMetaDyn> =
1090 Box::new(AssetMeta::<(), ()>::new(AssetAction::Ignore));
1091 (meta, None)
1092 }
1093 }
1094 };
1095 let meta_bytes = meta.serialize();
1096 (meta, meta_bytes, processor)
1097 }
1098 Err(err) => {
1099 return Err(ProcessError::ReadAssetMetaError {
1100 path: asset_path.clone(),
1101 err,
1102 })
1103 }
1104 };
1105
1106 let processed_writer = source.processed_writer()?;
1107
1108 let new_hash = {
1109 let mut reader_for_hash = reader.read(path).await.map_err(reader_err)?;
1112
1113 get_asset_hash(&meta_bytes, &mut reader_for_hash)
1114 .await
1115 .map_err(reader_err)?
1116 };
1117 let mut new_processed_info = ProcessedInfo {
1118 hash: new_hash,
1119 full_hash: new_hash,
1120 process_dependencies: Vec::new(),
1121 };
1122
1123 {
1124 let infos = self.data.processing_state.asset_infos.read().await;
1125 if let Some(current_processed_info) = infos
1126 .get(asset_path)
1127 .and_then(|i| i.processed_info.as_ref())
1128 && current_processed_info.hash == new_hash
1129 {
1130 let mut dependency_changed = false;
1131 for current_dep_info in ¤t_processed_info.process_dependencies {
1132 let live_hash = infos
1133 .get(¤t_dep_info.path)
1134 .and_then(|i| i.processed_info.as_ref())
1135 .map(|i| i.full_hash);
1136 if live_hash != Some(current_dep_info.full_hash) {
1137 dependency_changed = true;
1138 break;
1139 }
1140 }
1141 if !dependency_changed {
1142 return Ok(ProcessResult::SkippedNotChanged);
1143 }
1144 }
1145 }
1146
1147 let _transaction_lock = {
1150 let lock = {
1151 let mut infos = self.data.processing_state.asset_infos.write().await;
1152 let info = infos.get_or_insert(asset_path.clone());
1153 info.file_transaction_lock.clone()
1157 };
1158 lock.write_arc().await
1159 };
1160
1161 self.log_begin_processing(asset_path).await;
1165 if let Some(processor) = processor {
1166 let settings = source_meta.process_settings().unwrap();
1169
1170 let reader_for_process = reader.read(path).await.map_err(reader_err)?;
1180
1181 let mut writer = processed_writer.write(path).await.map_err(writer_err)?;
1182 let mut processed_meta = {
1183 let mut context = ProcessContext::new(
1184 self,
1185 asset_path,
1186 reader_for_process,
1187 &mut new_processed_info,
1188 );
1189 let process = processor.process(&mut context, settings, &mut *writer);
1190 #[cfg(feature = "trace")]
1191 let process = {
1192 let span = info_span!(
1193 "asset processing",
1194 processor = processor.type_path(),
1195 asset = asset_path.to_string(),
1196 );
1197 process.instrument(span)
1198 };
1199 process.await?
1200 };
1201
1202 writer
1203 .flush()
1204 .await
1205 .map_err(|e| ProcessError::AssetWriterError {
1206 path: asset_path.clone(),
1207 err: AssetWriterError::Io(e),
1208 })?;
1209
1210 let full_hash = get_full_asset_hash(
1211 new_hash,
1212 new_processed_info
1213 .process_dependencies
1214 .iter()
1215 .map(|i| i.full_hash),
1216 );
1217 new_processed_info.full_hash = full_hash;
1218 *processed_meta.processed_info_mut() = Some(new_processed_info.clone());
1219 let meta_bytes = processed_meta.serialize();
1220
1221 processed_writer
1222 .write_meta_bytes(path, &meta_bytes)
1223 .await
1224 .map_err(writer_err)?;
1225 } else {
1226 let mut reader_for_copy = reader.read(path).await.map_err(reader_err)?;
1228 let mut writer = processed_writer.write(path).await.map_err(writer_err)?;
1229 futures_lite::io::copy(&mut reader_for_copy, &mut writer)
1230 .await
1231 .map_err(|err| ProcessError::AssetWriterError {
1232 path: asset_path.clone_owned(),
1233 err: err.into(),
1234 })?;
1235 *source_meta.processed_info_mut() = Some(new_processed_info.clone());
1236 let meta_bytes = source_meta.serialize();
1237 processed_writer
1238 .write_meta_bytes(path, &meta_bytes)
1239 .await
1240 .map_err(writer_err)?;
1241 }
1242 self.log_end_processing(asset_path).await;
1243
1244 Ok(ProcessResult::Processed(new_processed_info))
1245 }
1246
1247 async fn validate_transaction_log_and_recover(&self) {
1248 let log_factory = self
1249 .data
1250 .log_factory
1251 .lock()
1252 .unwrap_or_else(PoisonError::into_inner)
1253 .take()
1256 .expect("the asset processor only starts once");
1257 if let Err(err) = validate_transaction_log(log_factory.as_ref()).await {
1258 let state_is_valid = match err {
1259 ValidateLogError::ReadLogError(err) => {
1260 error!("Failed to read processor log file. Processed assets cannot be validated so they must be re-generated {err}");
1261 false
1262 }
1263 ValidateLogError::UnrecoverableError => {
1264 error!("Encountered an unrecoverable error in the last run. Processed assets cannot be validated so they must be re-generated");
1265 false
1266 }
1267 ValidateLogError::EntryErrors(entry_errors) => {
1268 let mut state_is_valid = true;
1269 for entry_error in entry_errors {
1270 match entry_error {
1271 LogEntryError::DuplicateTransaction(_)
1272 | LogEntryError::EndedMissingTransaction(_) => {
1273 error!("{}", entry_error);
1274 state_is_valid = false;
1275 break;
1276 }
1277 LogEntryError::UnfinishedTransaction(path) => {
1278 debug!("Asset {path:?} did not finish processing. Clearing state for that asset");
1279 let mut unrecoverable_err = |message: &dyn core::fmt::Display| {
1280 error!("Failed to remove asset {path:?}: {message}");
1281 state_is_valid = false;
1282 };
1283 let Ok(source) = self.get_source(path.source()) else {
1284 unrecoverable_err(&"AssetSource does not exist");
1285 continue;
1286 };
1287 let Ok(processed_writer) = source.processed_writer() else {
1288 unrecoverable_err(&"AssetSource does not have a processed AssetWriter registered");
1289 continue;
1290 };
1291
1292 if let Err(err) = processed_writer.remove(path.path()).await {
1293 match err {
1294 AssetWriterError::Io(err) => {
1295 if err.kind() != ErrorKind::NotFound {
1297 unrecoverable_err(&err);
1298 }
1299 }
1300 }
1301 }
1302 if let Err(err) = processed_writer.remove_meta(path.path()).await {
1303 match err {
1304 AssetWriterError::Io(err) => {
1305 if err.kind() != ErrorKind::NotFound {
1307 unrecoverable_err(&err);
1308 }
1309 }
1310 }
1311 }
1312 }
1313 }
1314 }
1315 state_is_valid
1316 }
1317 };
1318
1319 if !state_is_valid {
1320 error!("Processed asset transaction log state was invalid and unrecoverable for some reason (see previous logs). Removing processed assets and starting fresh.");
1321 for source in self.sources().iter_processed() {
1322 let Ok(processed_writer) = source.processed_writer() else {
1323 continue;
1324 };
1325 if let Err(err) = processed_writer
1326 .remove_assets_in_directory(Path::new(""))
1327 .await
1328 {
1329 panic!("Processed assets were in a bad state. To correct this, the asset processor attempted to remove all processed assets and start from scratch. This failed. There is no way to continue. Try restarting, or deleting imported asset folder manually. {err}");
1330 }
1331 }
1332 }
1333 }
1334 let mut log = self.data.log.write().await;
1335 *log = match log_factory.create_new_log().await {
1336 Ok(log) => Some(log),
1337 Err(err) => panic!("Failed to initialize asset processor log. This cannot be recovered. Try restarting. If that doesn't work, try deleting processed asset folder. {}", err),
1338 };
1339 }
1340}
1341
1342impl AssetProcessorData {
1343 pub(crate) fn new(sources: Arc<AssetSources>, processing_state: Arc<ProcessingState>) -> Self {
1345 AssetProcessorData {
1346 processing_state,
1347 sources,
1348 log_factory: Mutex::new(Some(Box::new(FileTransactionLogFactory::default()))),
1349 log: Default::default(),
1350 processors: Default::default(),
1351 }
1352 }
1353
1354 pub fn set_log_factory(
1359 &self,
1360 factory: Box<dyn ProcessorTransactionLogFactory>,
1361 ) -> Result<(), SetTransactionLogFactoryError> {
1362 let mut log_factory = self
1363 .log_factory
1364 .lock()
1365 .unwrap_or_else(PoisonError::into_inner);
1366 if log_factory.is_none() {
1367 return Err(SetTransactionLogFactoryError::AlreadyInUse);
1370 }
1371
1372 *log_factory = Some(factory);
1373 Ok(())
1374 }
1375
1376 pub async fn wait_until_processed(&self, path: AssetPath<'static>) -> ProcessStatus {
1378 self.processing_state.wait_until_processed(path).await
1379 }
1380
1381 pub async fn wait_until_initialized(&self) {
1383 self.processing_state.wait_until_initialized().await;
1384 }
1385
1386 pub async fn wait_until_finished(&self) {
1388 self.processing_state.wait_until_finished().await;
1389 }
1390}
1391
1392impl ProcessingState {
1393 fn new() -> Self {
1395 let (mut initialized_sender, initialized_receiver) = async_broadcast::broadcast(1);
1396 let (mut finished_sender, finished_receiver) = async_broadcast::broadcast(1);
1397 initialized_sender.set_overflow(true);
1400 finished_sender.set_overflow(true);
1401
1402 Self {
1403 state: async_lock::RwLock::new(ProcessorState::Initializing),
1404 initialized_sender,
1405 initialized_receiver,
1406 finished_sender,
1407 finished_receiver,
1408 asset_infos: Default::default(),
1409 }
1410 }
1411
1412 async fn set_state(&self, state: ProcessorState) {
1414 let mut state_guard = self.state.write().await;
1415 let last_state = *state_guard;
1416 *state_guard = state;
1417 if last_state != ProcessorState::Finished && state == ProcessorState::Finished {
1418 self.finished_sender.broadcast(()).await.unwrap();
1419 } else if last_state != ProcessorState::Processing && state == ProcessorState::Processing {
1420 self.initialized_sender.broadcast(()).await.unwrap();
1421 }
1422 }
1423
1424 pub(crate) async fn get_state(&self) -> ProcessorState {
1426 *self.state.read().await
1427 }
1428
1429 pub(crate) async fn get_transaction_lock(
1432 &self,
1433 path: &AssetPath<'static>,
1434 ) -> Result<RwLockReadGuardArc<()>, AssetReaderError> {
1435 let lock = {
1436 let infos = self.asset_infos.read().await;
1437 let info = infos
1438 .get(path)
1439 .ok_or_else(|| AssetReaderError::NotFound(path.path().to_owned()))?;
1440 info.file_transaction_lock.clone()
1444 };
1445 Ok(lock.read_arc().await)
1446 }
1447
1448 pub(crate) async fn wait_until_processed(&self, path: AssetPath<'static>) -> ProcessStatus {
1450 self.wait_until_initialized().await;
1451 let mut receiver = {
1452 let infos = self.asset_infos.write().await;
1453 let info = infos.get(&path);
1454 match info {
1455 Some(info) => match info.status {
1456 Some(result) => return result,
1457 None => info.status_receiver.clone(),
1459 },
1460 None => return ProcessStatus::NonExistent,
1461 }
1462 };
1463 receiver.recv().await.unwrap()
1464 }
1465
1466 pub(crate) async fn wait_until_initialized(&self) {
1468 let receiver = {
1469 let state = self.state.read().await;
1470 match *state {
1471 ProcessorState::Initializing => {
1472 Some(self.initialized_receiver.clone())
1474 }
1475 _ => None,
1476 }
1477 };
1478
1479 if let Some(mut receiver) = receiver {
1480 receiver.recv().await.unwrap();
1481 }
1482 }
1483
1484 pub(crate) async fn wait_until_finished(&self) {
1486 let receiver = {
1487 let state = self.state.read().await;
1488 match *state {
1489 ProcessorState::Initializing | ProcessorState::Processing => {
1490 Some(self.finished_receiver.clone())
1492 }
1493 ProcessorState::Finished => None,
1494 }
1495 };
1496
1497 if let Some(mut receiver) = receiver {
1498 receiver.recv().await.unwrap();
1499 }
1500 }
1501}
1502
1503#[derive(Debug, Clone)]
1505pub enum ProcessResult {
1506 Processed(ProcessedInfo),
1507 SkippedNotChanged,
1508 Ignored,
1509}
1510
1511#[derive(Debug, PartialEq, Eq, Copy, Clone)]
1513pub enum ProcessStatus {
1514 Processed,
1515 Failed,
1516 NonExistent,
1517}
1518
1519#[derive(Debug)]
1521pub(crate) struct ProcessorAssetInfo {
1522 processed_info: Option<ProcessedInfo>,
1523 dependents: HashSet<AssetPath<'static>>,
1525 status: Option<ProcessStatus>,
1526 pub(crate) file_transaction_lock: Arc<async_lock::RwLock<()>>,
1536 status_sender: async_broadcast::Sender<ProcessStatus>,
1537 status_receiver: async_broadcast::Receiver<ProcessStatus>,
1538}
1539
1540impl Default for ProcessorAssetInfo {
1541 fn default() -> Self {
1542 let (mut status_sender, status_receiver) = async_broadcast::broadcast(1);
1543 status_sender.set_overflow(true);
1546 Self {
1547 processed_info: Default::default(),
1548 dependents: Default::default(),
1549 file_transaction_lock: Default::default(),
1550 status: None,
1551 status_sender,
1552 status_receiver,
1553 }
1554 }
1555}
1556
1557impl ProcessorAssetInfo {
1558 async fn update_status(&mut self, status: ProcessStatus) {
1559 if self.status != Some(status) {
1560 self.status = Some(status);
1561 self.status_sender.broadcast(status).await.unwrap();
1562 }
1563 }
1564}
1565
1566#[derive(Default, Debug)]
1570pub struct ProcessorAssetInfos {
1571 infos: HashMap<AssetPath<'static>, ProcessorAssetInfo>,
1576 non_existent_dependents: HashMap<AssetPath<'static>, HashSet<AssetPath<'static>>>,
1581}
1582
1583impl ProcessorAssetInfos {
1584 fn get_or_insert(&mut self, asset_path: AssetPath<'static>) -> &mut ProcessorAssetInfo {
1585 self.infos.entry(asset_path.clone()).or_insert_with(|| {
1586 let mut info = ProcessorAssetInfo::default();
1587 if let Some(dependents) = self.non_existent_dependents.remove(&asset_path) {
1589 info.dependents = dependents;
1590 }
1591 info
1592 })
1593 }
1594
1595 pub(crate) fn get(&self, asset_path: &AssetPath<'static>) -> Option<&ProcessorAssetInfo> {
1596 self.infos.get(asset_path)
1597 }
1598
1599 fn get_mut(&mut self, asset_path: &AssetPath<'static>) -> Option<&mut ProcessorAssetInfo> {
1600 self.infos.get_mut(asset_path)
1601 }
1602
1603 fn add_dependent(&mut self, asset_path: &AssetPath<'static>, dependent: AssetPath<'static>) {
1604 if let Some(info) = self.get_mut(asset_path) {
1605 info.dependents.insert(dependent);
1606 } else {
1607 let dependents = self
1608 .non_existent_dependents
1609 .entry(asset_path.clone())
1610 .or_default();
1611 dependents.insert(dependent);
1612 }
1613 }
1614
1615 async fn finish_processing(
1617 &mut self,
1618 asset_path: AssetPath<'static>,
1619 result: Result<ProcessResult, ProcessError>,
1620 reprocess_sender: async_channel::Sender<(AssetSourceId<'static>, PathBuf)>,
1621 ) {
1622 match result {
1623 Ok(ProcessResult::Processed(processed_info)) => {
1624 debug!("Finished processing \"{}\"", asset_path);
1625 let old_processed_info = self
1627 .infos
1628 .get_mut(&asset_path)
1629 .and_then(|i| i.processed_info.take());
1630 if let Some(old_processed_info) = old_processed_info {
1631 self.clear_dependencies(&asset_path, old_processed_info);
1632 }
1633
1634 for process_dependency_info in &processed_info.process_dependencies {
1636 self.add_dependent(&process_dependency_info.path, asset_path.to_owned());
1637 }
1638 let info = self.get_or_insert(asset_path);
1639 info.processed_info = Some(processed_info);
1640 info.update_status(ProcessStatus::Processed).await;
1641 let dependents = info.dependents.iter().cloned().collect::<Vec<_>>();
1642 for path in dependents {
1643 let _ = reprocess_sender
1644 .send((path.source().clone_owned(), path.path().to_owned()))
1645 .await;
1646 }
1647 }
1648 Ok(ProcessResult::SkippedNotChanged) => {
1649 debug!("Skipping processing (unchanged) \"{}\"", asset_path);
1650 let info = self.get_mut(&asset_path).expect("info should exist");
1651 info.update_status(ProcessStatus::Processed).await;
1658 }
1659 Ok(ProcessResult::Ignored) => {
1660 debug!("Skipping processing (ignored) \"{}\"", asset_path);
1661 }
1662 Err(ProcessError::ExtensionRequired) => {
1663 }
1665 Err(ProcessError::MissingAssetLoaderForExtension(_)) => {
1666 trace!("No loader found for {asset_path}");
1667 }
1668 Err(ProcessError::AssetReaderError {
1669 err: AssetReaderError::NotFound(_),
1670 ..
1671 }) => {
1672 trace!("No need to process asset {asset_path} because it does not exist");
1674 }
1675 Err(err) => {
1676 error!("Failed to process asset {asset_path}: {err}");
1677 if let ProcessError::AssetLoadError(AssetLoadError::AssetLoaderError(dependency)) =
1679 err
1680 {
1681 let info = self.get_mut(&asset_path).expect("info should exist");
1682 info.processed_info = Some(ProcessedInfo {
1683 hash: AssetHash::default(),
1684 full_hash: AssetHash::default(),
1685 process_dependencies: vec![],
1686 });
1687 self.add_dependent(dependency.path(), asset_path.to_owned());
1688 }
1689
1690 let info = self.get_mut(&asset_path).expect("info should exist");
1691 info.update_status(ProcessStatus::Failed).await;
1692 }
1693 }
1694 }
1695
1696 async fn remove(
1700 &mut self,
1701 asset_path: &AssetPath<'static>,
1702 ) -> Option<Arc<async_lock::RwLock<()>>> {
1703 let info = self.infos.remove(asset_path)?;
1704 if let Some(processed_info) = info.processed_info {
1705 self.clear_dependencies(asset_path, processed_info);
1706 }
1707 info.status_sender
1709 .broadcast(ProcessStatus::NonExistent)
1710 .await
1711 .unwrap();
1712 if !info.dependents.is_empty() {
1713 error!(
1714 "The asset at {asset_path} was removed, but it had assets that depend on it to be processed. Consider updating the path in the following assets: {:?}",
1715 info.dependents
1716 );
1717 self.non_existent_dependents
1718 .insert(asset_path.clone(), info.dependents);
1719 }
1720
1721 Some(info.file_transaction_lock)
1722 }
1723
1724 async fn rename(
1728 &mut self,
1729 old: &AssetPath<'static>,
1730 new: &AssetPath<'static>,
1731 new_task_sender: &async_channel::Sender<(AssetSourceId<'static>, PathBuf)>,
1732 ) -> Option<(Arc<async_lock::RwLock<()>>, Arc<async_lock::RwLock<()>>)> {
1733 let mut info = self.infos.remove(old)?;
1734 if !info.dependents.is_empty() {
1735 error!(
1743 "The asset at {old} was removed, but it had assets that depend on it to be processed. Consider updating the path in the following assets: {:?}",
1744 info.dependents
1745 );
1746 self.non_existent_dependents
1747 .insert(old.clone(), core::mem::take(&mut info.dependents));
1748 }
1749 if let Some(processed_info) = &info.processed_info {
1750 for dep in &processed_info.process_dependencies {
1752 if let Some(info) = self.infos.get_mut(&dep.path) {
1753 info.dependents.remove(old);
1754 info.dependents.insert(new.clone());
1755 } else if let Some(dependents) = self.non_existent_dependents.get_mut(&dep.path) {
1756 dependents.remove(old);
1757 dependents.insert(new.clone());
1758 }
1759 }
1760 }
1761 info.status_sender
1763 .broadcast(ProcessStatus::NonExistent)
1764 .await
1765 .unwrap();
1766 let new_info = self.get_or_insert(new.clone());
1767 new_info.processed_info = info.processed_info;
1768 new_info.status = info.status;
1769 if let Some(status) = new_info.status {
1771 new_info.status_sender.broadcast(status).await.unwrap();
1772 }
1773 let dependents = new_info.dependents.iter().cloned().collect::<Vec<_>>();
1774 let _ = new_task_sender
1776 .send((new.source().clone_owned(), new.path().to_owned()))
1777 .await;
1778 for dependent in dependents {
1779 let _ = new_task_sender
1781 .send((
1782 dependent.source().clone_owned(),
1783 dependent.path().to_owned(),
1784 ))
1785 .await;
1786 }
1787
1788 Some((
1789 info.file_transaction_lock,
1790 new_info.file_transaction_lock.clone(),
1791 ))
1792 }
1793
1794 fn clear_dependencies(&mut self, asset_path: &AssetPath<'static>, removed_info: ProcessedInfo) {
1795 for old_load_dep in removed_info.process_dependencies {
1796 if let Some(info) = self.infos.get_mut(&old_load_dep.path) {
1797 info.dependents.remove(asset_path);
1798 } else if let Some(dependents) =
1799 self.non_existent_dependents.get_mut(&old_load_dep.path)
1800 {
1801 dependents.remove(asset_path);
1802 }
1803 }
1804 }
1805}
1806
1807#[derive(Copy, Clone, PartialEq, Eq)]
1809pub enum ProcessorState {
1810 Initializing,
1814 Processing,
1816 Finished,
1818}
1819
1820#[derive(Error, Debug)]
1822pub enum InitializeError {
1823 #[error(transparent)]
1824 FailedToReadSourcePaths(AssetReaderError),
1825 #[error(transparent)]
1826 FailedToReadDestinationPaths(AssetReaderError),
1827 #[error("Failed to validate asset log: {0}")]
1828 ValidateLogError(#[from] ValidateLogError),
1829}
1830
1831#[derive(Error, Debug)]
1833pub enum SetTransactionLogFactoryError {
1834 #[error("Transaction log is already in use so setting the factory does nothing")]
1836 AlreadyInUse,
1837}
1838
1839#[derive(Error, Debug, PartialEq, Eq)]
1841pub enum GetProcessorError {
1842 #[error("The processor '{0}' does not exist")]
1844 Missing(String),
1845 #[error("The processor '{processor_short_name}' is ambiguous between several processors: {ambiguous_processor_names:?}")]
1847 Ambiguous {
1848 processor_short_name: String,
1850 ambiguous_processor_names: Vec<&'static str>,
1852 },
1853}
1854
1855impl From<GetProcessorError> for ProcessError {
1856 fn from(err: GetProcessorError) -> Self {
1857 match err {
1858 GetProcessorError::Missing(name) => Self::MissingProcessor(name),
1859 GetProcessorError::Ambiguous {
1860 processor_short_name,
1861 ambiguous_processor_names,
1862 } => Self::AmbiguousProcessor {
1863 processor_short_name,
1864 ambiguous_processor_names,
1865 },
1866 }
1867 }
1868}
1869
1870#[cfg(test)]
1871mod tests;