1use core::any::{Any, TypeId};
2
3use bevy_ecs::world::{unsafe_world_cell::UnsafeWorldCell, World};
4use bevy_reflect::{FromReflect, FromType, PartialReflect, Reflect};
5
6use crate::{Asset, AssetId, 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 PartialReflect) -> UntypedHandle,
26 insert: fn(&mut World, UntypedHandle, &dyn PartialReflect),
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 pub fn get_mut<'w>(
50 &self,
51 world: &'w mut World,
52 handle: UntypedHandle,
53 ) -> Option<&'w mut dyn Reflect> {
54 #[expect(
56 unsafe_code,
57 reason = "Use of unsafe `Self::get_unchecked_mut()` function."
58 )]
59 unsafe {
60 (self.get_unchecked_mut)(world.as_unsafe_world_cell(), handle)
61 }
62 }
63
64 #[expect(
92 unsafe_code,
93 reason = "This function calls unsafe code and has safety requirements."
94 )]
95 pub unsafe fn get_unchecked_mut<'w>(
96 &self,
97 world: UnsafeWorldCell<'w>,
98 handle: UntypedHandle,
99 ) -> Option<&'w mut dyn Reflect> {
100 unsafe { (self.get_unchecked_mut)(world, handle) }
102 }
103
104 pub fn add(&self, world: &mut World, value: &dyn PartialReflect) -> UntypedHandle {
106 (self.add)(world, value)
107 }
108 pub fn insert(&self, world: &mut World, handle: UntypedHandle, value: &dyn PartialReflect) {
110 (self.insert)(world, handle, value);
111 }
112
113 pub fn remove(&self, world: &mut World, handle: UntypedHandle) -> Option<Box<dyn Reflect>> {
115 (self.remove)(world, handle)
116 }
117
118 pub fn len(&self, world: &World) -> usize {
120 (self.len)(world)
121 }
122
123 pub fn is_empty(&self, world: &World) -> bool {
125 self.len(world) == 0
126 }
127
128 pub fn ids<'w>(&self, world: &'w World) -> impl Iterator<Item = UntypedAssetId> + 'w {
130 (self.ids)(world)
131 }
132}
133
134impl<A: Asset + FromReflect> FromType<A> for ReflectAsset {
135 fn from_type() -> Self {
136 ReflectAsset {
137 handle_type_id: TypeId::of::<Handle<A>>(),
138 assets_resource_type_id: TypeId::of::<Assets<A>>(),
139 get: |world, handle| {
140 let assets = world.resource::<Assets<A>>();
141 let asset = assets.get(&handle.typed_debug_checked());
142 asset.map(|asset| asset as &dyn Reflect)
143 },
144 get_unchecked_mut: |world, handle| {
145 #[expect(unsafe_code, reason = "Uses `UnsafeWorldCell::get_resource_mut()`.")]
148 let assets = unsafe { world.get_resource_mut::<Assets<A>>().unwrap().into_inner() };
149 let asset = assets.get_mut(&handle.typed_debug_checked());
150 asset.map(|asset| asset as &mut dyn Reflect)
151 },
152 add: |world, value| {
153 let mut assets = world.resource_mut::<Assets<A>>();
154 let value: A = FromReflect::from_reflect(value)
155 .expect("could not call `FromReflect::from_reflect` in `ReflectAsset::add`");
156 assets.add(value).untyped()
157 },
158 insert: |world, handle, value| {
159 let mut assets = world.resource_mut::<Assets<A>>();
160 let value: A = FromReflect::from_reflect(value)
161 .expect("could not call `FromReflect::from_reflect` in `ReflectAsset::set`");
162 assets.insert(&handle.typed_debug_checked(), value);
163 },
164 len: |world| {
165 let assets = world.resource::<Assets<A>>();
166 assets.len()
167 },
168 ids: |world| {
169 let assets = world.resource::<Assets<A>>();
170 Box::new(assets.ids().map(AssetId::untyped))
171 },
172 remove: |world, handle| {
173 let mut assets = world.resource_mut::<Assets<A>>();
174 let value = assets.remove(&handle.typed_debug_checked());
175 value.map(|value| Box::new(value) as Box<dyn Reflect>)
176 },
177 }
178 }
179}
180
181#[derive(Clone)]
207pub struct ReflectHandle {
208 asset_type_id: TypeId,
209 downcast_handle_untyped: fn(&dyn Any) -> Option<UntypedHandle>,
210 typed: fn(UntypedHandle) -> Box<dyn Reflect>,
211}
212impl ReflectHandle {
213 pub fn asset_type_id(&self) -> TypeId {
215 self.asset_type_id
216 }
217
218 pub fn downcast_handle_untyped(&self, handle: &dyn Any) -> Option<UntypedHandle> {
220 (self.downcast_handle_untyped)(handle)
221 }
222
223 pub fn typed(&self, handle: UntypedHandle) -> Box<dyn Reflect> {
226 (self.typed)(handle)
227 }
228}
229
230impl<A: Asset> FromType<Handle<A>> for ReflectHandle {
231 fn from_type() -> Self {
232 ReflectHandle {
233 asset_type_id: TypeId::of::<A>(),
234 downcast_handle_untyped: |handle: &dyn Any| {
235 handle
236 .downcast_ref::<Handle<A>>()
237 .map(|h| h.clone().untyped())
238 },
239 typed: |handle: UntypedHandle| Box::new(handle.typed_debug_checked::<A>()),
240 }
241 }
242}
243
244#[cfg(test)]
245mod tests {
246 use core::any::TypeId;
247
248 use crate as bevy_asset;
249 use crate::{Asset, AssetApp, AssetPlugin, ReflectAsset, UntypedHandle};
250 use bevy_app::App;
251 use bevy_ecs::reflect::AppTypeRegistry;
252 use bevy_reflect::Reflect;
253
254 #[derive(Asset, Reflect)]
255 struct AssetType {
256 field: String,
257 }
258
259 #[test]
260 fn test_reflect_asset_operations() {
261 let mut app = App::new();
262 app.add_plugins(AssetPlugin::default())
263 .init_asset::<AssetType>()
264 .register_asset_reflect::<AssetType>();
265
266 let reflect_asset = {
267 let type_registry = app.world().resource::<AppTypeRegistry>();
268 let type_registry = type_registry.read();
269
270 type_registry
271 .get_type_data::<ReflectAsset>(TypeId::of::<AssetType>())
272 .unwrap()
273 .clone()
274 };
275
276 let value = AssetType {
277 field: "test".into(),
278 };
279
280 let handle = reflect_asset.add(app.world_mut(), &value);
281 let strukt = reflect_asset
283 .get_mut(app.world_mut(), handle)
284 .unwrap()
285 .reflect_mut()
286 .as_struct()
287 .unwrap();
288 strukt
289 .field_mut("field")
290 .unwrap()
291 .apply(&String::from("edited"));
292
293 assert_eq!(reflect_asset.len(app.world()), 1);
294 let ids: Vec<_> = reflect_asset.ids(app.world()).collect();
295 assert_eq!(ids.len(), 1);
296
297 let fetched_handle = UntypedHandle::Weak(ids[0]);
298 let asset = reflect_asset
299 .get(app.world(), fetched_handle.clone_weak())
300 .unwrap();
301 assert_eq!(asset.downcast_ref::<AssetType>().unwrap().field, "edited");
302
303 reflect_asset
304 .remove(app.world_mut(), fetched_handle)
305 .unwrap();
306 assert_eq!(reflect_asset.len(app.world()), 0);
307 }
308}