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