1use crate::{
2 io::Writer, meta::Settings, transformer::TransformedAsset, Asset, AssetLoader,
3 ErasedLoadedAsset, Handle, LabeledAsset, UntypedHandle,
4};
5use alloc::boxed::Box;
6use atomicow::CowArc;
7use bevy_platform::collections::HashMap;
8use bevy_reflect::TypePath;
9use bevy_tasks::{BoxedFuture, ConditionalSendFuture};
10use core::{borrow::Borrow, hash::Hash, ops::Deref};
11use serde::{Deserialize, Serialize};
12
13pub trait AssetSaver: TypePath + Send + Sync + 'static {
20 type Asset: Asset;
22 type Settings: Settings + Default + Serialize + for<'a> Deserialize<'a>;
24 type OutputLoader: AssetLoader;
26 type Error: Into<Box<dyn core::error::Error + Send + Sync + 'static>>;
28
29 fn save(
32 &self,
33 writer: &mut Writer,
34 asset: SavedAsset<'_, Self::Asset>,
35 settings: &Self::Settings,
36 ) -> impl ConditionalSendFuture<
37 Output = Result<<Self::OutputLoader as AssetLoader>::Settings, Self::Error>,
38 >;
39}
40
41pub trait ErasedAssetSaver: Send + Sync + 'static {
43 fn save<'a>(
46 &'a self,
47 writer: &'a mut Writer,
48 asset: &'a ErasedLoadedAsset,
49 settings: &'a dyn Settings,
50 ) -> BoxedFuture<'a, Result<(), Box<dyn core::error::Error + Send + Sync + 'static>>>;
51
52 fn type_name(&self) -> &'static str;
54}
55
56impl<S: AssetSaver> ErasedAssetSaver for S {
57 fn save<'a>(
58 &'a self,
59 writer: &'a mut Writer,
60 asset: &'a ErasedLoadedAsset,
61 settings: &'a dyn Settings,
62 ) -> BoxedFuture<'a, Result<(), Box<dyn core::error::Error + Send + Sync + 'static>>> {
63 Box::pin(async move {
64 let settings = settings
65 .downcast_ref::<S::Settings>()
66 .expect("AssetLoader settings should match the loader type");
67 let saved_asset = SavedAsset::<S::Asset>::from_loaded(asset).unwrap();
68 if let Err(err) = self.save(writer, saved_asset, settings).await {
69 return Err(err.into());
70 }
71 Ok(())
72 })
73 }
74 fn type_name(&self) -> &'static str {
75 core::any::type_name::<S>()
76 }
77}
78
79pub struct SavedAsset<'a, A: Asset> {
81 value: &'a A,
82 labeled_assets: &'a HashMap<CowArc<'static, str>, LabeledAsset>,
83}
84
85impl<'a, A: Asset> Deref for SavedAsset<'a, A> {
86 type Target = A;
87
88 fn deref(&self) -> &Self::Target {
89 self.value
90 }
91}
92
93impl<'a, A: Asset> SavedAsset<'a, A> {
94 pub fn from_loaded(asset: &'a ErasedLoadedAsset) -> Option<Self> {
96 let value = asset.value.downcast_ref::<A>()?;
97 Some(SavedAsset {
98 value,
99 labeled_assets: &asset.labeled_assets,
100 })
101 }
102
103 pub fn from_transformed(asset: &'a TransformedAsset<A>) -> Self {
105 Self {
106 value: &asset.value,
107 labeled_assets: &asset.labeled_assets,
108 }
109 }
110
111 #[inline]
113 pub fn get(&self) -> &'a A {
114 self.value
115 }
116
117 pub fn get_labeled<B: Asset, Q>(&self, label: &Q) -> Option<SavedAsset<'_, B>>
119 where
120 CowArc<'static, str>: Borrow<Q>,
121 Q: ?Sized + Hash + Eq,
122 {
123 let labeled = self.labeled_assets.get(label)?;
124 let value = labeled.asset.value.downcast_ref::<B>()?;
125 Some(SavedAsset {
126 value,
127 labeled_assets: &labeled.asset.labeled_assets,
128 })
129 }
130
131 pub fn get_erased_labeled<Q>(&self, label: &Q) -> Option<&ErasedLoadedAsset>
133 where
134 CowArc<'static, str>: Borrow<Q>,
135 Q: ?Sized + Hash + Eq,
136 {
137 let labeled = self.labeled_assets.get(label)?;
138 Some(&labeled.asset)
139 }
140
141 pub fn get_untyped_handle<Q>(&self, label: &Q) -> Option<UntypedHandle>
143 where
144 CowArc<'static, str>: Borrow<Q>,
145 Q: ?Sized + Hash + Eq,
146 {
147 let labeled = self.labeled_assets.get(label)?;
148 Some(labeled.handle.clone())
149 }
150
151 pub fn get_handle<Q, B: Asset>(&self, label: &Q) -> Option<Handle<B>>
153 where
154 CowArc<'static, str>: Borrow<Q>,
155 Q: ?Sized + Hash + Eq,
156 {
157 let labeled = self.labeled_assets.get(label)?;
158 if let Ok(handle) = labeled.handle.clone().try_typed::<B>() {
159 return Some(handle);
160 }
161 None
162 }
163
164 pub fn iter_labels(&self) -> impl Iterator<Item = &str> {
166 self.labeled_assets.keys().map(|s| &**s)
167 }
168}