bevy_ecs/bundle/
impls.rs

1use core::{any::TypeId, iter};
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
13// SAFETY:
14// - `Bundle::component_ids` calls `ids` for C's component id (and nothing else)
15// - `Bundle::get_components` is called exactly once for C and passes the component's storage type based on its associated constant.
16unsafe impl<C: Component> Bundle for C {
17    fn component_ids(
18        components: &mut ComponentsRegistrator,
19    ) -> impl Iterator<Item = ComponentId> + use<C> {
20        iter::once(components.register_component::<C>())
21    }
22
23    fn get_component_ids(components: &Components) -> impl Iterator<Item = Option<ComponentId>> {
24        iter::once(components.get_id(TypeId::of::<C>()))
25    }
26}
27
28// SAFETY:
29// - `Bundle::from_components` calls `func` exactly once for C, which is the exact value returned by `Bundle::component_ids`.
30unsafe impl<C: Component> BundleFromComponents for C {
31    unsafe fn from_components<T, F>(ctx: &mut T, func: &mut F) -> Self
32    where
33        // Ensure that the `OwningPtr` is used correctly
34        F: for<'a> FnMut(&'a mut T) -> OwningPtr<'a>,
35        Self: Sized,
36    {
37        let ptr = func(ctx);
38        // Safety: The id given in `component_ids` is for `Self`
39        unsafe { ptr.read() }
40    }
41}
42
43impl<C: Component> DynamicBundle for C {
44    type Effect = ();
45    #[inline]
46    unsafe fn get_components(
47        ptr: MovingPtr<'_, Self>,
48        func: &mut impl FnMut(StorageType, OwningPtr<'_>),
49    ) -> Self::Effect {
50        func(C::STORAGE_TYPE, OwningPtr::from(ptr));
51    }
52
53    #[inline]
54    unsafe fn apply_effect(_ptr: MovingPtr<'_, MaybeUninit<Self>>, _entity: &mut EntityWorldMut) {}
55}
56
57macro_rules! tuple_impl {
58    ($(#[$meta:meta])* $(($index:tt, $name: ident, $alias: ident)),*) => {
59        #[expect(
60            clippy::allow_attributes,
61            reason = "This is a tuple-related macro; as such, the lints below may not always apply."
62        )]
63        #[allow(
64            unused_mut,
65            unused_variables,
66            reason = "Zero-length tuples won't use any of the parameters."
67        )]
68        $(#[$meta])*
69        // SAFETY:
70        // - `Bundle::component_ids` calls `ids` for each component type in the
71        // bundle, in the exact order that `DynamicBundle::get_components` is called.
72        // - `Bundle::from_components` calls `func` exactly once for each `ComponentId` returned by `Bundle::component_ids`.
73        // - `Bundle::get_components` is called exactly once for each member. Relies on the above implementation to pass the correct
74        //   `StorageType` into the callback.
75        unsafe impl<$($name: Bundle),*> Bundle for ($($name,)*) {
76            fn component_ids<'a>(components: &'a mut ComponentsRegistrator) -> impl Iterator<Item = ComponentId> + use<$($name,)*> {
77                iter::empty()$(.chain(<$name as Bundle>::component_ids(components)))*
78            }
79
80            fn get_component_ids(components: &Components) -> impl Iterator<Item = Option<ComponentId>> {
81                iter::empty()$(.chain(<$name as Bundle>::get_component_ids(components)))*
82            }
83        }
84
85        #[expect(
86            clippy::allow_attributes,
87            reason = "This is a tuple-related macro; as such, the lints below may not always apply."
88        )]
89        #[allow(
90            unused_mut,
91            unused_variables,
92            reason = "Zero-length tuples won't use any of the parameters."
93        )]
94        $(#[$meta])*
95        // SAFETY:
96        // - `Bundle::component_ids` calls `ids` for each component type in the
97        // bundle, in the exact order that `DynamicBundle::get_components` is called.
98        // - `Bundle::from_components` calls `func` exactly once for each `ComponentId` returned by `Bundle::component_ids`.
99        // - `Bundle::get_components` is called exactly once for each member. Relies on the above implementation to pass the correct
100        //   `StorageType` into the callback.
101        unsafe impl<$($name: BundleFromComponents),*> BundleFromComponents for ($($name,)*) {
102            #[allow(
103                clippy::unused_unit,
104                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."
105            )]
106            unsafe fn from_components<T, F>(ctx: &mut T, func: &mut F) -> Self
107            where
108                F: FnMut(&mut T) -> OwningPtr<'_>
109            {
110                #[allow(
111                    unused_unsafe,
112                    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."
113                )]
114                // SAFETY: Rust guarantees that tuple calls are evaluated 'left to right'.
115                // https://doc.rust-lang.org/reference/expressions.html#evaluation-order-of-operands
116                unsafe { ($(<$name as BundleFromComponents>::from_components(ctx, func),)*) }
117            }
118        }
119
120        #[expect(
121            clippy::allow_attributes,
122            reason = "This is a tuple-related macro; as such, the lints below may not always apply."
123        )]
124        #[allow(
125            unused_mut,
126            unused_variables,
127            reason = "Zero-length tuples won't use any of the parameters."
128        )]
129        $(#[$meta])*
130        impl<$($name: Bundle),*> DynamicBundle for ($($name,)*) {
131            type Effect = ($($name::Effect,)*);
132            #[allow(
133                clippy::unused_unit,
134                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."
135            )]
136            #[inline(always)]
137            unsafe fn get_components(ptr: MovingPtr<'_, Self>, func: &mut impl FnMut(StorageType, OwningPtr<'_>)) {
138                bevy_ptr::deconstruct_moving_ptr!({
139                    let tuple { $($index: $alias,)* } = ptr;
140                });
141                #[allow(
142                    unused_unsafe,
143                    reason = "Zero-length tuples will generate a function body equivalatent to (); however, this macro is meant for all applicable tuples, and as such it makes no sense to rewrite it just for that case."
144                )]
145                // SAFETY: Caller ensures requirements for calling `get_components` are met.
146                unsafe {
147                    $( $name::get_components($alias, func); )*
148                }
149            }
150
151            #[allow(
152                clippy::unused_unit,
153                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."
154            )]
155            #[inline(always)]
156            unsafe fn apply_effect(ptr: MovingPtr<'_, MaybeUninit<Self>>, entity: &mut EntityWorldMut) {
157                bevy_ptr::deconstruct_moving_ptr!({
158                    let MaybeUninit::<tuple> { $($index: $alias,)* } = ptr;
159                });
160                #[allow(
161                    unused_unsafe,
162                    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."
163                )]
164                // SAFETY: Caller ensures requirements for calling `apply_effect` are met.
165                unsafe {
166                    $( $name::apply_effect($alias, entity); )*
167                }
168            }
169        }
170
171        $(#[$meta])*
172        impl<$($name: NoBundleEffect),*> NoBundleEffect for ($($name,)*) {}
173    }
174}
175
176all_tuples_enumerated!(
177    #[doc(fake_variadic)]
178    tuple_impl,
179    0,
180    15,
181    B,
182    field_
183);