hecs_component_provider/
lib.rs

1//! Easily define behavior for sets of components when using the [hecs](https://github.com/Ralith/hecs/) ECS library.
2//!
3//! ```rust
4//! use hecs_component_provider::{
5//!     default_trait_impl, gen_tuple_query_component_providers,
6//!     ComponentProvider, ComponentProviderMut
7//! };
8//!
9//! struct Position(f32, f32);
10//! struct Velocity(f32, f32);
11//! struct Enemy { shot_count: i32 };
12//!
13//! // implement a behavior for all entities that provide the required components
14//! #[default_trait_impl]
15//! trait ApplyVelocity: ComponentProviderMut<Position> + ComponentProvider<Velocity> {
16//!     fn apply_velocity(&mut self, dt: f32) {
17//!         let &Velocity(vx, vy) = self.get();
18//!         let position: &mut Position = self.get_mut();
19//!         position.0 += vx * dt;
20//!         position.1 += vy * dt;
21//!     }
22//! }
23//!
24//! let mut world = hecs::World::new();
25//! world.spawn((Position(1.0, 2.0), Velocity(0.7, 0.8)));
26//!
27//! // prepare a query that returns entities with the components required for the behavior
28//! gen_tuple_query_component_providers!(
29//!     MovableQuery,
30//!     (&mut Position, &Velocity)
31//! );
32//!
33//! let dt = 0.1;
34//! # let mut found = false;
35//! for (_, mut entity) in world.query_mut::<MovableQuery>() {
36//!     // apply the behavior to the entity
37//!     entity.apply_velocity(dt);
38//!
39//!     let position = entity.0;
40//!     assert_eq!(position.0, 1.07);
41//!     assert_eq!(position.1, 2.08);
42//!     # found = true;
43//! }
44//! # assert!(found);
45//!
46//!
47//! // behaviors can depend on one another
48//! #[default_trait_impl]
49//! trait EnemyBehaviors: ApplyVelocity + ComponentProviderMut<Enemy> {
50//!     fn shoot_and_move(&mut self, dt: f32) {
51//!         self.shoot();
52//!         self.apply_velocity(dt);
53//!     }
54//!
55//!     fn shoot(&mut self) {
56//!         let enemy: &mut Enemy = self.get_mut();
57//!         enemy.shot_count += 1;
58//!     }
59//! }
60//!
61//! world.spawn((Enemy { shot_count: 0 }, Position(2.0, 3.0), Velocity(-0.7, -0.8)));
62//!
63//! // queries can be prepared using structs instead of tuples
64//! #[derive(hecs::Query, ComponentProvider)]
65//! struct EnemyQuery<'a> {
66//!     enemy: &'a mut Enemy,
67//!     position: &'a mut Position,
68//!     velocity: &'a Velocity,
69//! }
70//!
71//! let dt = 0.1;
72//! # let mut found = false;
73//! for (_, mut entity) in world.query_mut::<EnemyQuery>() {
74//!     // apply the behavior to the entity
75//!     entity.shoot_and_move(dt);
76//!
77//!     assert_eq!(entity.enemy.shot_count, 1);
78//!     assert_eq!(entity.position.0, 1.93);
79//!     assert_eq!(entity.position.1, 2.92);
80//!     # found = true;
81//! }
82//! # assert!(found);
83//! ```
84
85#[doc(hidden)]
86pub use gensym::gensym;
87
88pub trait ComponentProvider<Component> {
89    fn get(&self) -> &Component;
90}
91
92pub trait ComponentProviderMut<Component>: ComponentProvider<Component> {
93    fn get_mut(&mut self) -> &mut Component;
94}
95
96pub trait ComponentProviderOptional<Component> {
97    fn get_optional(&self) -> Option<&Component>;
98}
99
100pub trait ComponentProviderOptionalMut<Component>: ComponentProviderOptional<Component> {
101    fn get_optional_mut(&mut self) -> Option<&mut Component>;
102}
103
104/// Attach to a component struct to implement [`ComponentProvider`] and [`ComponentProviderMut`] for the struct
105///
106/// This allows behavior methods that require only a single component to be called on the struct
107/// itself, even if the struct is not the direct result of a query.
108///
109/// ```
110/// use hecs_component_provider::{default_trait_impl, ComponentProvider, SelfComponentProvider};
111///
112/// #[derive(SelfComponentProvider)]
113/// struct Position(i32, i32);
114///
115/// #[default_trait_impl]
116/// trait DistanceCalculator: ComponentProvider<Position> {
117///     fn calculate_distance_squared_to(&self, other: &Position) -> i32 {
118///         let position: &Position = self.get();
119///         let dx = other.0 - position.0;
120///         let dy = other.1 - position.1;
121///         dx * dx + dy * dy
122///     }
123/// }
124///
125/// let position = Position(1, 2);
126/// let other = Position(5, 8);
127/// let distance_squared = position.calculate_distance_squared_to(&other);
128/// assert_eq!(distance_squared, 52);
129/// ```
130pub use hecs_component_provider_macros::SelfComponentProvider;
131
132/// Attach to a struct that derives [`hecs::Bundle`] or [`hecs::Query`] to generate component provider implementations for those structs.
133///
134/// ```
135/// use hecs_component_provider::{
136///     default_trait_impl, ComponentProvider, ComponentProviderMut
137/// };
138///
139/// #[derive(Debug, Eq, PartialEq)]
140/// struct Position(i32, i32);
141/// #[derive(Debug, Eq, PartialEq)]
142/// struct Velocity(i32, i32);
143///
144/// #[default_trait_impl]
145/// trait ApplyVelocity: ComponentProviderMut<Position> + ComponentProvider<Velocity> {
146///     fn apply_velocity(&mut self) {
147///         // bind with let to disambiguate between component providers for Position and Velocity:
148///         let &Velocity(vx, vy) = self.get();
149///         // or use fully qualified syntax:
150///         assert!(matches!(ComponentProvider::<Velocity>::get(self), &Velocity(_, _)));
151
152///         let position: &mut Position = self.get_mut();
153///         position.0 += vx;
154///         position.1 += vy;
155///     }
156/// }
157///
158/// #[derive(hecs::Bundle, ComponentProvider)]
159/// struct Entity {
160///     position: Position,
161///     velocity: Velocity,
162/// }
163///
164/// #[derive(hecs::Query, ComponentProvider)]
165/// struct MovableQuery<'a> {
166///     position: &'a mut Position,
167///     velocity: &'a Velocity,
168/// }
169///
170/// let mut world = hecs::World::new();
171/// let mut spawn_entity = Entity {
172///     position: Position(10, 20),
173///     velocity: Velocity(7, 8)
174/// };
175/// spawn_entity.apply_velocity(); // uses ComponentProvider implementation on Bundle
176/// world.spawn(spawn_entity);
177///
178/// for (_, mut entity) in world.query_mut::<MovableQuery>() {
179///     entity.apply_velocity(); // uses ComponentProvider implementation on Query
180///     let position: &Position = entity.get();
181///     assert_eq!(position, &Position(24, 36)); // apply_velocity has been applied twice by now
182/// }
183/// ```
184pub use hecs_component_provider_macros::ComponentProvider;
185
186/// Implement the attached trait for all types that implement the trait's supertraits
187///
188/// This can be used to reduce boilerplate when defining behavior traits. Attach the macro to a behavior
189/// trait that requires certain components to exist and the trait will be implemented for all entities
190/// that have those components.
191///
192/// ```
193/// use hecs_component_provider::{default_trait_impl, ComponentProviderMut};
194///
195/// #[default_trait_impl]
196/// trait MoveRight: ComponentProviderMut<Position> {
197///    fn move_right(&mut self) {
198///        let position: &mut Position = self.get_mut();
199///        position.0 += 1;
200///    }
201/// }
202/// // `default_trait_impl` generates the following, which would otherwise need to be manually provided:
203/// // impl <T> move_right for T where T: ComponentProviderMut<Position> {}
204///
205/// # #[derive(hecs_component_provider::SelfComponentProvider)]
206/// # struct Position(i32, i32);
207/// # let mut position = Position(1, 2);
208/// # position.move_right();
209/// # assert_eq!(position.0, 2);
210/// ```
211pub use hecs_component_provider_macros::default_trait_impl;
212
213/// Prepare a tuple query that includes component provider implementations for the returned entities
214///
215/// The first argument to the macro is the name of the query type that you would like to generate,
216/// which can then be passed to the query methods on [`hecs::World`].
217/// The second argument is the tuple of components that the query will return.
218///
219/// ```
220/// use hecs_component_provider::{
221///     gen_tuple_query_component_providers, ComponentProvider, ComponentProviderMut
222/// };
223///
224/// #[derive(Debug, Eq, PartialEq)]
225/// struct Position(i32, i32);
226/// #[derive(Debug, Eq, PartialEq)]
227/// struct Velocity(i32, i32);
228///
229/// let mut world = hecs::World::new();
230/// world.spawn((Position(10, 20), Velocity(7, 8)));
231///
232/// gen_tuple_query_component_providers!(MovableQuery, (&mut Position, &Velocity));
233///
234/// for (_, mut entity) in world.query_mut::<MovableQuery>() {
235///     assert_eq!(entity.get_mut(), &mut Position(10, 20));
236///
237///     // bind with let to disambiguate between component providers for Position and Velocity:
238///     let _velocity: &Velocity = entity.get();
239///     // or use fully qualified syntax:
240///     assert_eq!(ComponentProvider::<Velocity>::get(&entity), &Velocity(7, 8));
241/// }
242/// ```
243#[macro_export]
244macro_rules! gen_tuple_query_component_providers {
245    // Uses a TT muncher to add lifetimes: https://users.rust-lang.org/t/macro-to-replace-type-parameters/17903/2
246
247    // Open parenthesis.
248    ($alias:ident, @($($stack:tt)*) ($($first:tt)*) $($rest:tt)*) => {
249        gen_tuple_query_component_providers!($alias, @(() $($stack)*) $($first)* __paren $($rest)*);
250    };
251
252    // Close parenthesis.
253    ($alias:ident, @(($($close:tt)*) ($($top:tt)*) $($stack:tt)*) __paren $($rest:tt)*) => {
254        gen_tuple_query_component_providers!($alias, @(($($top)* ($($close)*)) $($stack)*) $($rest)*);
255    };
256
257    // Replace `&` token with `& 'a`.
258    ($alias:ident, @(($($top:tt)*) $($stack:tt)*) & $($rest:tt)*) => {
259        gen_tuple_query_component_providers!($alias, @(($($top)* &'a) $($stack)*) $($rest)*);
260    };
261
262    // Replace `&&` token with `& 'a & 'a`.
263    ($alias:ident, @(($($top:tt)*) $($stack:tt)*) && $($rest:tt)*) => {
264        gen_tuple_query_component_providers!($alias, @(($($top)* &'a &'a) $($stack)*) $($rest)*);
265    };
266
267    // Munch a token that is not `&`.
268    ($alias:ident, @(($($top:tt)*) $($stack:tt)*) $first:tt $($rest:tt)*) => {
269        gen_tuple_query_component_providers!($alias, @(($($top)* $first) $($stack)*) $($rest)*);
270    };
271
272    // Done.
273    ($alias:ident, @(($($top:tt)+))) => {
274        $crate::gensym! { gen_tuple_query_component_providers!(impl $alias, $($top)+) }
275    };
276
277    ($gensym:ident, impl $alias:ident, ($($tt:tt)*)) => {
278        #[derive(::hecs::Query, $crate::ComponentProvider)]
279        struct $gensym<'a>($($tt)*);
280        type $alias<'a> = $gensym<'a>;
281    };
282
283    // Begin with an empty stack.
284    ($alias:ident, $($input:tt)+) => {
285        gen_tuple_query_component_providers!($alias, @(()) $($input)*);
286    };
287}