moonshine_kind/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use bevy_ecs::{prelude::*, query::QueryFilter};
4
5pub mod prelude {
6    pub use crate::{kind, Kind, OfKind};
7    pub use crate::{GetInstanceCommands, InstanceCommands};
8    pub use crate::{Instance, InstanceMut, InstanceRef};
9    pub use crate::{KindBundle, SpawnInstance, SpawnInstanceWorld};
10}
11
12/// A type which represents the kind of an [`Entity`].
13///
14/// An entity is of kind `T` if it matches [`Query<Entity, <T as Kind>::Filter>`][`Query`].
15///
16/// By default, an entity with a [`Component`] of type `T` is also of kind `T`.
17///
18/// # Examples
19/// ```
20/// # use bevy::prelude::*;
21/// # use moonshine_kind::prelude::*;
22///
23/// #[derive(Component)]
24/// struct Apple;
25///
26/// #[derive(Component)]
27/// struct Orange;
28///
29/// struct Fruit;
30///
31/// impl Kind for Fruit {
32///     type Filter = Or<(With<Apple>, With<Orange>)>;
33/// }
34///
35/// fn fruits(query: Query<Instance<Fruit>>) {
36///     for fruit in query.iter() {
37///         println!("{fruit:?} is a fruit!");
38///     }
39/// }
40///
41/// # bevy_ecs::system::assert_is_system(fruits);
42/// ```
43pub trait Kind: 'static + Send + Sized + Sync {
44    type Filter: QueryFilter;
45
46    /// Returns the debug name of this kind.
47    ///
48    /// By default, this is the short type name (without path) of this kind.
49    fn debug_name() -> String {
50        moonshine_util::get_short_name(std::any::type_name::<Self>())
51    }
52}
53
54impl<T: Component> Kind for T {
55    type Filter = With<T>;
56}
57
58/// Represents the kind of any [`Entity`].
59///
60/// See [`Instance<Any>`] for more information on usage.
61pub struct Any;
62
63impl Kind for Any {
64    type Filter = ();
65}
66
67mod instance;
68
69pub use instance::*;
70
71/// A trait which allows safe casting from one [`Kind`] to another.
72///
73/// # Usage
74/// Prefer to use the [`kind`] macro to implement this trait.
75pub trait CastInto<T: Kind>: Kind {
76    fn cast_into(instance: Instance<Self>) -> Instance<T>;
77}
78
79impl<T: Kind> CastInto<T> for T {
80    fn cast_into(instance: Instance<Self>) -> Instance<Self> {
81        instance
82    }
83}
84
85/// A macro to safely implement [`CastInto`] for a pair of related [`Kind`]s.
86///
87/// See [`CastInto`] for more information.
88///
89/// # Usage
90/// ```
91/// # use bevy::prelude::*;
92/// # use moonshine_kind::prelude::*;
93///
94/// struct Fruit;
95///
96/// impl Kind for Fruit {
97///    type Filter = With<Apple>;
98/// }
99///
100/// #[derive(Component)]
101/// struct Apple;
102///
103/// // We can guarantee all entities with an `Apple` component are of kind `Fruit`:
104/// kind!(Apple is Fruit);
105///
106/// fn eat_apple(apple: Instance<Apple>) {
107///    println!("Crunch!");
108///    // SAFE: Because we said so.
109///    eat_fruit(apple.cast_into());
110/// }
111///
112/// fn eat_fruit(fruit: Instance<Fruit>) {
113///    println!("Yum!");
114/// }
115/// ```
116#[macro_export]
117macro_rules! kind {
118    ($T:ident is $U:ty) => {
119        impl $crate::CastInto<$U> for $T {
120            fn cast_into(instance: $crate::Instance<Self>) -> $crate::Instance<$U> {
121                // SAFE: Because we said so!
122                unsafe { instance.cast_into_unchecked() }
123            }
124        }
125    };
126}
127
128/// A short alias for using a [`Kind`] as a [`QueryFilter`].
129///
130/// # Example
131/// ```
132/// # use bevy::prelude::*;
133/// # use moonshine_kind::prelude::*;
134///
135/// #[derive(Component)]
136/// struct Apple;
137///
138/// fn count_apples(query: Query<(), OfKind<Apple>>) -> usize {
139///     query.iter().count()
140/// }
141///
142/// # bevy_ecs::system::assert_is_system(count_apples);
143/// ```
144pub type OfKind<T> = <T as Kind>::Filter;
145
146/// A [`Bundle`] which represents a [`Kind`].
147///
148/// # Usage
149/// This trait is used to allow spawning an [`Instance<T>`] where `T` is [`<Self as KindBundle>::Kind`][`KindBundle::Kind`].
150///
151/// Any [`Component`] is automatically a kind bundle of its own kind.
152///
153/// See [`SpawnInstance`] for more information.
154pub trait KindBundle: Bundle {
155    /// The [`Kind`] represented by this [`Bundle`].
156    type Kind: Kind;
157}
158
159impl<T: Kind + Bundle> KindBundle for T {
160    type Kind = T;
161}
162
163/// Extension trait to safely spawn an [`Instance<T>`] using [`Commands`] where `T` associated with a [`KindBundle`].
164pub trait SpawnInstance {
165    /// Spawns a new [`Instance<T>`] using its associated [`KindBundle`].
166    ///
167    /// # Example
168    /// ```
169    /// # use bevy::prelude::*;
170    /// # use moonshine_kind::prelude::*;
171    ///
172    /// #[derive(Component)]
173    /// struct Apple;
174    ///
175    /// fn spawn_apple(mut commands: Commands) {
176    ///     let apple: Instance<Apple> = commands.spawn_instance(Apple).instance();
177    ///     println!("Spawned {apple:?}!");
178    /// }
179    ///
180    /// # bevy_ecs::system::assert_is_system(spawn_apple);
181    fn spawn_instance<T: KindBundle>(&mut self, _: T) -> InstanceCommands<'_, T::Kind>;
182}
183
184impl SpawnInstance for Commands<'_, '_> {
185    fn spawn_instance<T: KindBundle>(&mut self, bundle: T) -> InstanceCommands<'_, T::Kind> {
186        let entity = self.spawn(bundle).id();
187        // SAFE: `entity` must be a valid instance of `T::Kind`.
188        unsafe { InstanceCommands::from_entity_unchecked(self.entity(entity)) }
189    }
190}
191
192/// Extension trait to safely spawn an [`Instance<T>`] using [`World`] where `T` associated with a [`KindBundle`].
193pub trait SpawnInstanceWorld {
194    /// Spawns a new [`Instance<T>`] using its associated [`KindBundle`].
195    ///
196    /// # Example
197    /// ```
198    /// # use bevy::prelude::*;
199    /// # use moonshine_kind::prelude::*;
200    ///
201    /// #[derive(Component)]
202    /// struct Apple;
203    ///
204    /// fn spawn_apple(world: &mut World) {
205    ///     let apple: Instance<Apple> = world.spawn_instance(Apple).instance();
206    ///     println!("Spawned {apple:?}!");
207    /// }
208    fn spawn_instance<T: KindBundle>(&mut self, _: T) -> InstanceMutItem<'_, T::Kind>
209    where
210        T::Kind: Component;
211}
212
213impl SpawnInstanceWorld for World {
214    fn spawn_instance<T: KindBundle>(&mut self, bundle: T) -> InstanceMutItem<'_, T::Kind>
215    where
216        T::Kind: Component,
217    {
218        let entity = self.spawn(bundle).id();
219        // SAFE: `entity` must be a valid instance of kind `T`.
220        InstanceMutItem::from_entity(self, entity).unwrap()
221    }
222}
223
224#[cfg(test)]
225mod tests {
226    use super::*;
227    use bevy_ecs::system::RunSystemOnce;
228
229    fn count<T: Kind>(query: Query<Instance<T>>) -> usize {
230        query.iter().count()
231    }
232
233    #[test]
234    fn kind_with() {
235        #[derive(Component)]
236        struct Foo;
237
238        let mut world = World::new();
239        world.spawn(Foo);
240        assert_eq!(world.run_system_once(count::<Foo>).unwrap(), 1);
241    }
242
243    #[test]
244    fn kind_without() {
245        #[derive(Component)]
246        struct Foo;
247
248        struct NotFoo;
249
250        impl Kind for NotFoo {
251            type Filter = Without<Foo>;
252        }
253
254        let mut world = World::new();
255        world.spawn(Foo);
256        assert_eq!(world.run_system_once(count::<NotFoo>).unwrap(), 0);
257    }
258
259    #[test]
260    fn kind_multi() {
261        #[derive(Component)]
262        struct Foo;
263
264        #[derive(Component)]
265        struct Bar;
266
267        let mut world = World::new();
268        world.spawn((Foo, Bar));
269        assert_eq!(world.run_system_once(count::<Foo>).unwrap(), 1);
270        assert_eq!(world.run_system_once(count::<Bar>).unwrap(), 1);
271    }
272
273    #[test]
274    fn kind_cast() {
275        #[derive(Component)]
276        struct Foo;
277
278        #[derive(Component)]
279        struct Bar;
280
281        kind!(Foo is Bar);
282
283        let any = Instance::<Any>::PLACEHOLDER;
284        let foo = Instance::<Foo>::PLACEHOLDER;
285        let bar = foo.cast_into::<Bar>();
286        assert!(foo.cast_into_any() == any);
287        assert!(bar.cast_into_any() == any);
288        // assert!(any.cast_into::<Foo>() == foo); // <-- Must not compile!
289        // assert!(bar.cast_into::<Foo>() == foo); // <-- Must not compile!
290        assert!(bar.entity() == foo.entity());
291    }
292}