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