unlit_ecs/bundle.rs
1//! Bundles: the set of components an entity is spawned with.
2//!
3//! A bundle is either a [`DynamicBundle`], which the [`crate::bundle!`] macro writes,
4//! or a type of the caller's own that implements [`Bundle`] over its components. It
5//! is not a tuple: every `'static` type is a component, tuples included, so a bare
6//! `(a, b)` handed to `spawn` names one component rather than two. Writing
7//! `bundle![a, b]` is what names two.
8//!
9//! The macro takes its items one at a time and each item is one component, so a
10//! parenthesised item is a tuple component rather than a list to unpack: a
11//! bundle only spreads when it is written as `..value`.
12//!
13//! The same component cannot appear twice in a bundle; spawning with duplicates
14//! panics.
15
16use core::any::{Any, TypeId};
17
18use crate::column::{AnyColumn, Column};
19
20/// One component of a bundle: its type, the name and column constructor the
21/// world registers it under, and the value box the archetype takes.
22///
23/// The type-keyed facts sit in one entry rather than in parallel arrays, so a
24/// component's type is written once and the arrays cannot fall out of step
25/// when they are sorted.
26pub(crate) struct BundleComponent {
27 pub(crate) type_id: TypeId,
28 pub(crate) name: &'static str,
29 pub(crate) column: fn() -> Box<dyn AnyColumn>,
30 pub(crate) value: Box<dyn Any>,
31}
32
33/// The components of one bundle, sorted by type.
34pub(crate) type Components = Vec<BundleComponent>;
35
36/// A bundle assembled at runtime.
37///
38/// This is what [`crate::bundle!`] writes and what a [`Bundle`] implementation
39/// builds its components into. A type of the caller's own is the usual way to
40/// spawn an object; reach for this one when the components are only known once
41/// the program runs.
42///
43/// It also carries the name of the entity it will spawn, which by default is the
44/// source position of the [`crate::bundle!`] that built it.
45pub struct DynamicBundle {
46 components: Components,
47 name: &'static str,
48}
49
50impl Default for DynamicBundle {
51 fn default() -> Self {
52 Self::new()
53 }
54}
55
56impl DynamicBundle {
57 /// An empty bundle with no name, for one built by hand.
58 ///
59 /// [`crate::bundle!`] is what supplies a name, so a bundle filled in by
60 /// hand names its entity after nothing: give it one with
61 /// [`DynamicBundle::named`], or spawn it with
62 /// [`ScopeMut::spawn_named`](crate::ScopeMut::spawn_named).
63 pub fn new() -> Self {
64 Self {
65 components: Vec::new(),
66 name: "",
67 }
68 }
69
70 /// Name the bundle, replacing the one [`crate::bundle!`] gave it.
71 ///
72 /// [`crate::bundle!`] names every bundle it builds after the source
73 /// position that wrote it, and a spawn gives that name to the entity. A
74 /// bundle assembled at runtime has no such position to speak of, so it
75 /// names itself here instead.
76 #[must_use]
77 pub fn named(self, name: &'static str) -> Self {
78 Self {
79 components: self.components,
80 name,
81 }
82 }
83
84 /// Add one component.
85 ///
86 /// The value is stored under its own type, so a tuple pushed here stays one
87 /// component.
88 pub fn push_component<C: 'static>(&mut self, value: C) {
89 self.components.push(BundleComponent {
90 type_id: TypeId::of::<C>(),
91 name: core::any::type_name::<C>(),
92 column: Column::<C>::eraser(),
93 value: Box::new(value),
94 });
95 }
96
97 /// Add every component of `bundle` to this one.
98 ///
99 /// This is how a caller gathers several bundles into one spawn: the
100 /// components keep their own types, so a bundle that holds a tuple
101 /// contributes that tuple as a single component, exactly as spawning it
102 /// alone would.
103 pub fn push_bundle<B: Bundle>(&mut self, bundle: B) {
104 self.components.extend(bundle.into_bundle().components);
105 }
106
107 /// The components in type order.
108 ///
109 /// # Panics
110 ///
111 /// When the same component appears twice. The duplicate is caught here
112 /// rather than at spawn so that every path into an archetype, queued or
113 /// immediate, reports it from the call that wrote the bundle.
114 #[track_caller]
115 pub(crate) fn finish(self) -> Components {
116 let mut components = self.components;
117 sort_components(&mut components);
118 components
119 }
120}
121
122/// Put a bundle's components in type order, which is the order an archetype
123/// stores its columns in.
124///
125/// # Panics
126///
127/// When the same component appears twice.
128#[track_caller]
129fn sort_components(components: &mut [BundleComponent]) {
130 components.sort_unstable_by_key(|component| component.type_id);
131 for pair in components.windows(2) {
132 assert_ne!(
133 pair[0].type_id, pair[1].type_id,
134 "a bundle cannot contain the same component twice",
135 );
136 }
137}
138
139/// A set of components to spawn an entity with.
140///
141/// Implemented for a [`DynamicBundle`], which is what the [`crate::bundle!`] macro
142/// produces, and for any type of a caller's own that can say what its
143/// components are. It is deliberately not implemented for tuples: every
144/// `'static` type is a component, so a tuple impl would make `(a, b)` mean two
145/// components in one place and one component in another.
146///
147/// A caller's own type is the usual way to spawn an object: give it the fields
148/// it is built from, and build its components in [`Bundle::into_bundle`].
149///
150/// ````rust
151/// # use unlit_ecs::{Bundle, DynamicBundle, World, bundle};
152/// struct Cube {
153/// size: f32,
154/// }
155///
156/// impl Bundle for Cube {
157/// fn into_bundle(self) -> DynamicBundle {
158/// bundle![self.size]
159/// }
160/// }
161///
162/// let mut world = World::new();
163/// let cube = world.root_scope_mut().spawn(Cube { size: 1.0 });
164/// assert!(cube.name().ends_with("::Cube"), "the entity is named after the object");
165/// ````
166#[diagnostic::on_unimplemented(
167 message = "`{Self}` is not a bundle",
168 label = "implement `Bundle` for it, or build it with `bundle!`",
169 note = "`(a, b)` is a single component here: every `'static` type is one, tuples included. Write `bundle![a, b]` for two components."
170)]
171pub trait Bundle {
172 /// The name the spawned entity takes: the bundle's own type by default.
173 ///
174 /// A [`DynamicBundle`] has a name of its own — the source position of the
175 /// [`crate::bundle!`] that wrote it, or the label [`DynamicBundle::named`]
176 /// gave it — and answers with that. A type of the caller's own has no such
177 /// label to speak of, so it names the entity after itself, which is the
178 /// object the entity is. A bundle that names itself after something else
179 /// overrides this.
180 fn name(&self) -> &'static str {
181 core::any::type_name::<Self>()
182 }
183
184 /// The dynamic bundle holding exactly this bundle's components.
185 ///
186 /// The bundle supplies the whole bundle, so there is no target to append to
187 /// or overwrite. A caller gathering several bundles into one spawn pushes
188 /// each onto a [`DynamicBundle`] with [`DynamicBundle::push_bundle`].
189 fn into_bundle(self) -> DynamicBundle;
190}
191
192impl Bundle for DynamicBundle {
193 fn name(&self) -> &'static str {
194 self.name
195 }
196
197 fn into_bundle(self) -> DynamicBundle {
198 self
199 }
200}
201
202/// Builds a bundle from the components written between the brackets.
203///
204/// The items are components, separated by commas: `bundle![a, b]` is two
205/// components. A list of one needs no comma, so `bundle![a]` is the single
206/// component `a`. An item is one component however it is written, because every
207/// `'static` type is one: `bundle![(a, b)]` names a single tuple component, and
208/// `bundle![()]` a single empty-tuple one.
209///
210/// The macro is written with brackets, `bundle![a, b]`, throughout this crate
211/// and its dependents. Rust accepts any of the three delimiters after the `!`,
212/// so the brackets are a convention that keeps a bundle's items, which are not
213/// call arguments, visibly distinct.
214///
215/// ````rust
216/// # use unlit_ecs::{Has, World, bundle};
217/// let mut world = World::new();
218/// let entity = world.root_scope_mut().spawn(bundle![1u32, 2.0f32, true]);
219/// assert!(world.root_scope().query_one::<Has<u32>>(entity).unwrap().get());
220/// assert!(world.root_scope().query_one::<Has<f32>>(entity).unwrap().get());
221/// assert!(world.root_scope().query_one::<Has<bool>>(entity).unwrap().get());
222///
223/// // A tuple is one component.
224/// let entity = world.root_scope_mut().spawn(bundle![(1u8, 2u8)]);
225/// assert!(world.root_scope().query_one::<Has<(u8, u8)>>(entity).unwrap().get());
226/// assert!(!world.root_scope().query_one::<Has<u8>>(entity).unwrap().get());
227/// ````
228///
229/// A bundle written as `..value` is unwrapped into its own components, the way
230/// spawning it alone would. The syntax is needed because the macro walks the
231/// source and cannot tell a bundle from a component; written plainly, a bundle
232/// is one component.
233///
234/// ````rust
235/// # use unlit_ecs::{DynamicBundle, Has, World, bundle};
236/// let mut gathered = DynamicBundle::new();
237/// gathered.push_component(1u32);
238/// let mut world = World::new();
239/// let entity = world.root_scope_mut().spawn(bundle![..gathered, true]);
240/// assert!(world.root_scope().query_one::<Has<u32>>(entity).unwrap().get());
241/// assert!(world.root_scope().query_one::<Has<bool>>(entity).unwrap().get());
242/// ````
243///
244/// The width is bounded by the compiler's recursion limit rather than by a
245/// fixed count: each item costs a level of macro recursion, so roughly a
246/// hundred components fit at the default limit of 128. A wider bundle is built
247/// by raising `#![recursion_limit]` in the calling crate, or by pushing onto
248/// a [`DynamicBundle`] directly.
249///
250/// A type that appears twice panics when the bundle is spawned.
251#[macro_export]
252macro_rules! bundle {
253 [] => { $crate::DynamicBundle::new().named(concat!(file!(), ":", line!())) };
254 // A bundle written as `..value`, unwrapped into its own components. It is
255 // matched before the `$leaf:expr` arms because `..value` is not an
256 // expression.
257 [@list $bundle:ident, ..$spread:expr, $($rest:tt)*] => {
258 $bundle.push_bundle($spread);
259 $crate::bundle![@list $bundle, $($rest)*];
260 };
261 [@list $bundle:ident, ..$spread:expr] => { $bundle.push_bundle($spread) };
262 // One component. It is matched opaquely, so a tuple is one component and a
263 // group is never taken apart.
264 [@list $bundle:ident, $leaf:expr, $($rest:tt)*] => {
265 $bundle.push_component($leaf);
266 $crate::bundle![@list $bundle, $($rest)*];
267 };
268 [@list $bundle:ident,] => {};
269 [@list $bundle:ident, $leaf:expr] => { $bundle.push_component($leaf) };
270 [$($item:tt)+] => {{
271 let mut bundle = $crate::DynamicBundle::new().named(concat!(file!(), ":", line!()));
272 $crate::bundle![@list bundle, $($item)+];
273 bundle
274 }};
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280 use crate::World;
281
282 /// Gathering bundles keeps each one's own components: a bundle's leaves are
283 /// its own, and a component pushed whole that is itself a tuple stays one
284 /// component.
285 #[test]
286 fn push_bundle_gathers_bundles() {
287 let mut world = World::new();
288 let mut bundle = DynamicBundle::new();
289 bundle.push_component(1u32);
290 bundle.push_bundle(crate::bundle![2.0f32, true]);
291 bundle.push_component((7u8, 8u8));
292 let entity = world.root_scope_mut().spawn(bundle);
293 assert_eq!(
294 world
295 .root_scope()
296 .query_one::<&u32>(entity)
297 .map(|mut value| *value.get()),
298 Ok(1)
299 );
300 assert_eq!(
301 world
302 .root_scope()
303 .query_one::<&f32>(entity)
304 .map(|mut value| *value.get()),
305 Ok(2.0)
306 );
307 assert_eq!(
308 world
309 .root_scope()
310 .query_one::<&bool>(entity)
311 .map(|mut value| *value.get()),
312 Ok(true)
313 );
314 // The pair went in as one component, the way spawning it alone would
315 // have.
316 assert_eq!(
317 world
318 .root_scope()
319 .query_one::<&(u8, u8)>(entity)
320 .map(|mut value| *value.get()),
321 Ok((7, 8))
322 );
323 }
324
325 /// A duplicate is reported by the call that finishes the bundle, whether it
326 /// was written twice in one list or gathered from a spread.
327 #[test]
328 #[should_panic(expected = "same component twice")]
329 fn a_duplicate_panics_when_the_bundle_is_finished() {
330 let mut bundle = DynamicBundle::new();
331 bundle.push_component(1u32);
332 bundle.push_bundle(crate::bundle![2u32]);
333 bundle.finish();
334 }
335
336 /// A wide bundle still finishes in type order, each component whole.
337 #[test]
338 fn a_wide_bundle_finishes_in_type_order() {
339 let mut bundle = DynamicBundle::new();
340 bundle.push_component(1u8);
341 bundle.push_component(2u16);
342 bundle.push_component(3u32);
343 bundle.push_component(4u64);
344 bundle.push_component(5i8);
345 bundle.push_component(6i16);
346 bundle.push_component(7i32);
347 bundle.push_component(8i64);
348 bundle.push_component(9.0f32);
349 bundle.push_component(10.0f64);
350 let finished = bundle.finish();
351 assert_eq!(finished.len(), 10);
352 assert!(
353 finished
354 .windows(2)
355 .all(|pair| pair[0].type_id < pair[1].type_id),
356 "the components come out in type order",
357 );
358 }
359}