1use std::any::{Any, TypeId};
2
3use bevy_ecs::world::{unsafe_world_cell::UnsafeWorldCell, World};
4use bevy_reflect::{FromReflect, FromType, Reflect};
5
6use crate::{Asset, Assets, Handle, UntypedAssetId, UntypedHandle};
7
8#[derive(Clone)]
16pub struct ReflectAsset {
17 handle_type_id: TypeId,
18 assets_resource_type_id: TypeId,
19
20 get: fn(&World, UntypedHandle) -> Option<&dyn Reflect>,
21 get_unchecked_mut: unsafe fn(UnsafeWorldCell<'_>, UntypedHandle) -> Option<&mut dyn Reflect>,
25 add: fn(&mut World, &dyn Reflect) -> UntypedHandle,
26 insert: fn(&mut World, UntypedHandle, &dyn Reflect),
27 len: fn(&World) -> usize,
28 ids: for<'w> fn(&'w World) -> Box<dyn Iterator<Item = UntypedAssetId> + 'w>,
29 remove: fn(&mut World, UntypedHandle) -> Option<Box<dyn Reflect>>,
30}
31
32impl ReflectAsset {
33 pub fn handle_type_id(&self) -> TypeId {
35 self.handle_type_id
36 }
37
38 pub fn assets_resource_type_id(&self) -> TypeId {
40 self.assets_resource_type_id
41 }
42
43 pub fn get<'w>(&self, world: &'w World, handle: UntypedHandle) -> Option<&'w dyn Reflect> {
45 (self.get)(world, handle)
46 }
47
48 #[allow(unsafe_code)]
50 pub fn get_mut<'w>(
51 &self,
52 world: &'w mut World,
53 handle: UntypedHandle,
54 ) -> Option<&'w mut dyn Reflect> {
55 unsafe { (self.get_unchecked_mut)(world.as_unsafe_world_cell(), handle) }
57 }
58
59 #[allow(unsafe_code)]
87 pub unsafe fn get_unchecked_mut<'w>(
88 &self,
89 world: UnsafeWorldCell<'w>,
90 handle: UntypedHandle,
91 ) -> Option<&'w mut dyn Reflect> {
92 unsafe { (self.get_unchecked_mut)(world, handle) }
94 }
95
96 pub fn add(&self, world: &mut World, value: &dyn Reflect) -> UntypedHandle {
98 (self.add)(world, value)
99 }
100 pub fn insert(&self, world: &mut World, handle: UntypedHandle, value: &dyn Reflect) {
102 (self.insert)(world, handle, value);
103 }
104
105 pub fn remove(&self, world: &mut World, handle: UntypedHandle) -> Option<Box<dyn Reflect>> {
107 (self.remove)(world, handle)
108 }
109
110 #[allow(clippy::len_without_is_empty)] pub fn len(&self, world: &World) -> usize {
113 (self.len)(world)
114 }
115
116 pub fn is_empty(&self, world: &World) -> bool {
118 self.len(world) == 0
119 }
120
121 pub fn ids<'w>(&self, world: &'w World) -> impl Iterator<Item = UntypedAssetId> + 'w {
123 (self.ids)(world)
124 }
125}
126
127impl<A: Asset + FromReflect> FromType<A> for ReflectAsset {
128 fn from_type() -> Self {
129 ReflectAsset {
130 handle_type_id: TypeId::of::<Handle<A>>(),
131 assets_resource_type_id: TypeId::of::<Assets<A>>(),
132 get: |world, handle| {
133 let assets = world.resource::<Assets<A>>();
134 let asset = assets.get(&handle.typed_debug_checked());
135 asset.map(|asset| asset as &dyn Reflect)
136 },
137 get_unchecked_mut: |world, handle| {
138 #[allow(unsafe_code)]
141 let assets = unsafe { world.get_resource_mut::<Assets<A>>().unwrap().into_inner() };
142 let asset = assets.get_mut(&handle.typed_debug_checked());
143 asset.map(|asset| asset as &mut dyn Reflect)
144 },
145 add: |world, value| {
146 let mut assets = world.resource_mut::<Assets<A>>();
147 let value: A = FromReflect::from_reflect(value)
148 .expect("could not call `FromReflect::from_reflect` in `ReflectAsset::add`");
149 assets.add(value).untyped()
150 },
151 insert: |world, handle, value| {
152 let mut assets = world.resource_mut::<Assets<A>>();
153 let value: A = FromReflect::from_reflect(value)
154 .expect("could not call `FromReflect::from_reflect` in `ReflectAsset::set`");
155 assets.insert(&handle.typed_debug_checked(), value);
156 },
157 len: |world| {
158 let assets = world.resource::<Assets<A>>();
159 assets.len()
160 },
161 ids: |world| {
162 let assets = world.resource::<Assets<A>>();
163 Box::new(assets.ids().map(|i| i.untyped()))
164 },
165 remove: |world, handle| {
166 let mut assets = world.resource_mut::<Assets<A>>();
167 let value = assets.remove(&handle.typed_debug_checked());
168 value.map(|value| Box::new(value) as Box<dyn Reflect>)
169 },
170 }
171 }
172}
173
174#[derive(Clone)]
200pub struct ReflectHandle {
201 asset_type_id: TypeId,
202 downcast_handle_untyped: fn(&dyn Any) -> Option<UntypedHandle>,
203 typed: fn(UntypedHandle) -> Box<dyn Reflect>,
204}
205impl ReflectHandle {
206 pub fn asset_type_id(&self) -> TypeId {
208 self.asset_type_id
209 }
210
211 pub fn downcast_handle_untyped(&self, handle: &dyn Any) -> Option<UntypedHandle> {
213 (self.downcast_handle_untyped)(handle)
214 }
215
216 pub fn typed(&self, handle: UntypedHandle) -> Box<dyn Reflect> {
219 (self.typed)(handle)
220 }
221}
222
223impl<A: Asset> FromType<Handle<A>> for ReflectHandle {
224 fn from_type() -> Self {
225 ReflectHandle {
226 asset_type_id: TypeId::of::<A>(),
227 downcast_handle_untyped: |handle: &dyn Any| {
228 handle
229 .downcast_ref::<Handle<A>>()
230 .map(|h| h.clone().untyped())
231 },
232 typed: |handle: UntypedHandle| Box::new(handle.typed_debug_checked::<A>()),
233 }
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use std::any::TypeId;
240
241 use crate as bevy_asset;
242 use crate::{Asset, AssetApp, AssetPlugin, ReflectAsset, UntypedHandle};
243 use bevy_app::App;
244 use bevy_ecs::reflect::AppTypeRegistry;
245 use bevy_reflect::{Reflect, ReflectMut};
246
247 #[derive(Asset, Reflect)]
248 struct AssetType {
249 field: String,
250 }
251
252 #[test]
253 fn test_reflect_asset_operations() {
254 let mut app = App::new();
255 app.add_plugins(AssetPlugin::default())
256 .init_asset::<AssetType>()
257 .register_asset_reflect::<AssetType>();
258
259 let reflect_asset = {
260 let type_registry = app.world().resource::<AppTypeRegistry>();
261 let type_registry = type_registry.read();
262
263 type_registry
264 .get_type_data::<ReflectAsset>(TypeId::of::<AssetType>())
265 .unwrap()
266 .clone()
267 };
268
269 let value = AssetType {
270 field: "test".into(),
271 };
272
273 let handle = reflect_asset.add(app.world_mut(), &value);
274 let ReflectMut::Struct(strukt) = reflect_asset
275 .get_mut(app.world_mut(), handle)
276 .unwrap()
277 .reflect_mut()
278 else {
279 unreachable!();
280 };
281 strukt
282 .field_mut("field")
283 .unwrap()
284 .apply(&String::from("edited"));
285
286 assert_eq!(reflect_asset.len(app.world()), 1);
287 let ids: Vec<_> = reflect_asset.ids(app.world()).collect();
288 assert_eq!(ids.len(), 1);
289
290 let fetched_handle = UntypedHandle::Weak(ids[0]);
291 let asset = reflect_asset
292 .get(app.world(), fetched_handle.clone_weak())
293 .unwrap();
294 assert_eq!(asset.downcast_ref::<AssetType>().unwrap().field, "edited");
295
296 reflect_asset
297 .remove(app.world_mut(), fetched_handle)
298 .unwrap();
299 assert_eq!(reflect_asset.len(app.world()), 0);
300 }
301}