Skip to main content

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}