1use core::any::TypeId;
2
3use bevy_ptr::{MovingPtr, OwningPtr};
4use core::mem::MaybeUninit;
5use variadics_please::all_tuples_enumerated;
6
7use crate::{
8 bundle::{Bundle, BundleFromComponents, DynamicBundle, NoBundleEffect},
9 component::{Component, ComponentId, Components, ComponentsRegistrator, StorageType},
10 world::EntityWorldMut,
11};
12
13unsafe impl<C: Component> Bundle for C {
17 fn component_ids(components: &mut ComponentsRegistrator, ids: &mut impl FnMut(ComponentId)) {
18 ids(components.register_component::<C>());
19 }
20
21 fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option<ComponentId>)) {
22 ids(components.get_id(TypeId::of::<C>()));
23 }
24}
25
26unsafe impl<C: Component> BundleFromComponents for C {
29 unsafe fn from_components<T, F>(ctx: &mut T, func: &mut F) -> Self
30 where
31 F: for<'a> FnMut(&'a mut T) -> OwningPtr<'a>,
33 Self: Sized,
34 {
35 let ptr = func(ctx);
36 unsafe { ptr.read() }
38 }
39}
40
41impl<C: Component> DynamicBundle for C {
42 type Effect = ();
43 #[inline]
44 unsafe fn get_components(
45 ptr: MovingPtr<'_, Self>,
46 func: &mut impl FnMut(StorageType, OwningPtr<'_>),
47 ) -> Self::Effect {
48 func(C::STORAGE_TYPE, OwningPtr::from(ptr));
49 }
50
51 #[inline]
52 unsafe fn apply_effect(_ptr: MovingPtr<'_, MaybeUninit<Self>>, _entity: &mut EntityWorldMut) {}
53}
54
55macro_rules! tuple_impl {
56 ($(#[$meta:meta])* $(($index:tt, $name: ident, $alias: ident)),*) => {
57 #[expect(
58 clippy::allow_attributes,
59 reason = "This is a tuple-related macro; as such, the lints below may not always apply."
60 )]
61 #[allow(
62 unused_mut,
63 unused_variables,
64 reason = "Zero-length tuples won't use any of the parameters."
65 )]
66 $(#[$meta])*
67 unsafe impl<$($name: Bundle),*> Bundle for ($($name,)*) {
74 fn component_ids(components: &mut ComponentsRegistrator, ids: &mut impl FnMut(ComponentId)){
75 $(<$name as Bundle>::component_ids(components, ids);)*
76 }
77
78 fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option<ComponentId>)){
79 $(<$name as Bundle>::get_component_ids(components, ids);)*
80 }
81 }
82
83 #[expect(
84 clippy::allow_attributes,
85 reason = "This is a tuple-related macro; as such, the lints below may not always apply."
86 )]
87 #[allow(
88 unused_mut,
89 unused_variables,
90 reason = "Zero-length tuples won't use any of the parameters."
91 )]
92 $(#[$meta])*
93 unsafe impl<$($name: BundleFromComponents),*> BundleFromComponents for ($($name,)*) {
100 #[allow(
101 clippy::unused_unit,
102 reason = "Zero-length tuples will generate a function body equivalent to `()`; however, this macro is meant for all applicable tuples, and as such it makes no sense to rewrite it just for that case."
103 )]
104 unsafe fn from_components<T, F>(ctx: &mut T, func: &mut F) -> Self
105 where
106 F: FnMut(&mut T) -> OwningPtr<'_>
107 {
108 #[allow(
109 unused_unsafe,
110 reason = "Zero-length tuples will not run anything in the unsafe block. Additionally, rewriting this to move the () outside of the unsafe would require putting the safety comment inside the tuple, hurting readability of the code."
111 )]
112 unsafe { ($(<$name as BundleFromComponents>::from_components(ctx, func),)*) }
115 }
116 }
117
118 #[expect(
119 clippy::allow_attributes,
120 reason = "This is a tuple-related macro; as such, the lints below may not always apply."
121 )]
122 #[allow(
123 unused_mut,
124 unused_variables,
125 reason = "Zero-length tuples won't use any of the parameters."
126 )]
127 $(#[$meta])*
128 impl<$($name: Bundle),*> DynamicBundle for ($($name,)*) {
129 type Effect = ($($name::Effect,)*);
130 #[allow(
131 clippy::unused_unit,
132 reason = "Zero-length tuples will generate a function body equivalent to `()`; however, this macro is meant for all applicable tuples, and as such it makes no sense to rewrite it just for that case."
133 )]
134 #[inline(always)]
135 unsafe fn get_components(ptr: MovingPtr<'_, Self>, func: &mut impl FnMut(StorageType, OwningPtr<'_>)) {
136 bevy_ptr::deconstruct_moving_ptr!({
137 let tuple { $($index: $alias,)* } = ptr;
138 });
139 $( $name::get_components($alias, func); )*
141 }
142
143 #[allow(
144 clippy::unused_unit,
145 reason = "Zero-length tuples will generate a function body equivalent to `()`; however, this macro is meant for all applicable tuples, and as such it makes no sense to rewrite it just for that case."
146 )]
147 #[inline(always)]
148 unsafe fn apply_effect(ptr: MovingPtr<'_, MaybeUninit<Self>>, entity: &mut EntityWorldMut) {
149 bevy_ptr::deconstruct_moving_ptr!({
150 let MaybeUninit::<tuple> { $($index: $alias,)* } = ptr;
151 });
152 $( $name::apply_effect($alias, entity); )*
154 }
155 }
156
157 $(#[$meta])*
158 impl<$($name: NoBundleEffect),*> NoBundleEffect for ($($name,)*) {}
159 }
160}
161
162all_tuples_enumerated!(
163 #[doc(fake_variadic)]
164 tuple_impl,
165 0,
166 15,
167 B,
168 field_
169);