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::{
8 Asset, AssetId, Assets, Handle, InvalidGenerationError, UntypedAssetId, UntypedHandle,
9};
10
11#[derive(Clone)]
19pub struct ReflectAsset {
20 handle_type_id: TypeId,
21 assets_resource_type_id: TypeId,
22
23 get: fn(&World, UntypedAssetId) -> Option<&dyn Reflect>,
24 get_unchecked_mut: unsafe fn(UnsafeWorldCell<'_>, UntypedAssetId) -> Option<&mut dyn Reflect>,
28 add: fn(&mut World, &dyn PartialReflect) -> UntypedHandle,
29 insert:
30 fn(&mut World, UntypedAssetId, &dyn PartialReflect) -> Result<(), InvalidGenerationError>,
31 len: fn(&World) -> usize,
32 ids: for<'w> fn(&'w World) -> Box<dyn Iterator<Item = UntypedAssetId> + 'w>,
33 remove: fn(&mut World, UntypedAssetId) -> Option<Box<dyn Reflect>>,
34}
35
36impl ReflectAsset {
37 pub fn handle_type_id(&self) -> TypeId {
39 self.handle_type_id
40 }
41
42 pub fn assets_resource_type_id(&self) -> TypeId {
44 self.assets_resource_type_id
45 }
46
47 pub fn get<'w>(
49 &self,
50 world: &'w World,
51 asset_id: impl Into<UntypedAssetId>,
52 ) -> Option<&'w dyn Reflect> {
53 (self.get)(world, asset_id.into())
54 }
55
56 pub fn get_mut<'w>(
58 &self,
59 world: &'w mut World,
60 asset_id: impl Into<UntypedAssetId>,
61 ) -> Option<&'w mut dyn Reflect> {
62 #[expect(
64 unsafe_code,
65 reason = "Use of unsafe `Self::get_unchecked_mut()` function."
66 )]
67 unsafe {
68 (self.get_unchecked_mut)(world.as_unsafe_world_cell(), asset_id.into())
69 }
70 }
71
72 #[expect(
100 unsafe_code,
101 reason = "This function calls unsafe code and has safety requirements."
102 )]
103 pub unsafe fn get_unchecked_mut<'w>(
104 &self,
105 world: UnsafeWorldCell<'w>,
106 asset_id: impl Into<UntypedAssetId>,
107 ) -> Option<&'w mut dyn Reflect> {
108 unsafe { (self.get_unchecked_mut)(world, asset_id.into()) }
110 }
111
112 pub fn add(&self, world: &mut World, value: &dyn PartialReflect) -> UntypedHandle {
114 (self.add)(world, value)
115 }
116 pub fn insert(
118 &self,
119 world: &mut World,
120 asset_id: impl Into<UntypedAssetId>,
121 value: &dyn PartialReflect,
122 ) -> Result<(), InvalidGenerationError> {
123 (self.insert)(world, asset_id.into(), value)
124 }
125
126 pub fn remove(
128 &self,
129 world: &mut World,
130 asset_id: impl Into<UntypedAssetId>,
131 ) -> Option<Box<dyn Reflect>> {
132 (self.remove)(world, asset_id.into())
133 }
134
135 pub fn len(&self, world: &World) -> usize {
137 (self.len)(world)
138 }
139
140 pub fn is_empty(&self, world: &World) -> bool {
142 self.len(world) == 0
143 }
144
145 pub fn ids<'w>(&self, world: &'w World) -> impl Iterator<Item = UntypedAssetId> + 'w {
147 (self.ids)(world)
148 }
149}
150
151impl<A: Asset + FromReflect> FromType<A> for ReflectAsset {
152 fn from_type() -> Self {
153 ReflectAsset {
154 handle_type_id: TypeId::of::<Handle<A>>(),
155 assets_resource_type_id: TypeId::of::<Assets<A>>(),
156 get: |world, asset_id| {
157 let assets = world.resource::<Assets<A>>();
158 let asset = assets.get(asset_id.typed_debug_checked());
159 asset.map(|asset| asset as &dyn Reflect)
160 },
161 get_unchecked_mut: |world, asset_id| {
162 #[expect(unsafe_code, reason = "Uses `UnsafeWorldCell::get_resource_mut()`.")]
165 let assets = unsafe { world.get_resource_mut::<Assets<A>>().unwrap().into_inner() };
166 let asset = assets.get_mut(asset_id.typed_debug_checked());
167 asset.map(|asset| asset as &mut dyn Reflect)
168 },
169 add: |world, value| {
170 let mut assets = world.resource_mut::<Assets<A>>();
171 let value: A = FromReflect::from_reflect(value)
172 .expect("could not call `FromReflect::from_reflect` in `ReflectAsset::add`");
173 assets.add(value).untyped()
174 },
175 insert: |world, asset_id, value| {
176 let mut assets = world.resource_mut::<Assets<A>>();
177 let value: A = FromReflect::from_reflect(value)
178 .expect("could not call `FromReflect::from_reflect` in `ReflectAsset::set`");
179 assets.insert(asset_id.typed_debug_checked(), value)
180 },
181 len: |world| {
182 let assets = world.resource::<Assets<A>>();
183 assets.len()
184 },
185 ids: |world| {
186 let assets = world.resource::<Assets<A>>();
187 Box::new(assets.ids().map(AssetId::untyped))
188 },
189 remove: |world, asset_id| {
190 let mut assets = world.resource_mut::<Assets<A>>();
191 let value = assets.remove(asset_id.typed_debug_checked());
192 value.map(|value| Box::new(value) as Box<dyn Reflect>)
193 },
194 }
195 }
196}
197
198#[derive(Clone)]
224pub struct ReflectHandle {
225 asset_type_id: TypeId,
226 downcast_handle_untyped: fn(&dyn Any) -> Option<UntypedHandle>,
227 typed: fn(UntypedHandle) -> Box<dyn Reflect>,
228}
229
230impl ReflectHandle {
231 pub fn asset_type_id(&self) -> TypeId {
233 self.asset_type_id
234 }
235
236 pub fn downcast_handle_untyped(&self, handle: &dyn Any) -> Option<UntypedHandle> {
238 (self.downcast_handle_untyped)(handle)
239 }
240
241 pub fn typed(&self, handle: UntypedHandle) -> Box<dyn Reflect> {
244 (self.typed)(handle)
245 }
246}
247
248impl<A: Asset> FromType<Handle<A>> for ReflectHandle {
249 fn from_type() -> Self {
250 ReflectHandle {
251 asset_type_id: TypeId::of::<A>(),
252 downcast_handle_untyped: |handle: &dyn Any| {
253 handle
254 .downcast_ref::<Handle<A>>()
255 .map(|h| h.clone().untyped())
256 },
257 typed: |handle: UntypedHandle| Box::new(handle.typed_debug_checked::<A>()),
258 }
259 }
260}
261
262#[cfg(test)]
263mod tests {
264 use alloc::{string::String, vec::Vec};
265 use core::any::TypeId;
266
267 use crate::{Asset, AssetApp, AssetPlugin, ReflectAsset};
268 use bevy_app::App;
269 use bevy_ecs::reflect::AppTypeRegistry;
270 use bevy_reflect::Reflect;
271
272 #[derive(Asset, Reflect)]
273 struct AssetType {
274 field: String,
275 }
276
277 #[test]
278 fn test_reflect_asset_operations() {
279 let mut app = App::new();
280 app.add_plugins(AssetPlugin::default())
281 .init_asset::<AssetType>()
282 .register_asset_reflect::<AssetType>();
283
284 let reflect_asset = {
285 let type_registry = app.world().resource::<AppTypeRegistry>();
286 let type_registry = type_registry.read();
287
288 type_registry
289 .get_type_data::<ReflectAsset>(TypeId::of::<AssetType>())
290 .unwrap()
291 .clone()
292 };
293
294 let value = AssetType {
295 field: "test".into(),
296 };
297
298 let handle = reflect_asset.add(app.world_mut(), &value);
299 let strukt = reflect_asset
301 .get_mut(app.world_mut(), &handle)
302 .unwrap()
303 .reflect_mut()
304 .as_struct()
305 .unwrap();
306 strukt
307 .field_mut("field")
308 .unwrap()
309 .apply(&String::from("edited"));
310
311 assert_eq!(reflect_asset.len(app.world()), 1);
312 let ids: Vec<_> = reflect_asset.ids(app.world()).collect();
313 assert_eq!(ids.len(), 1);
314 let id = ids[0];
315
316 let asset = reflect_asset.get(app.world(), id).unwrap();
317 assert_eq!(asset.downcast_ref::<AssetType>().unwrap().field, "edited");
318
319 reflect_asset.remove(app.world_mut(), id).unwrap();
320 assert_eq!(reflect_asset.len(app.world()), 0);
321 }
322}