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