Required components accept const values (#16720) (#18309)

# Objective

Const values should be more ergonomic to insert, since this is too
verbose
``` rust
#[derive(Component)]
#[require(
    LockedAxes(||LockedAxes::ROTATION_LOCKED),
)]
pub struct CharacterController;
```
instead, users can now abbreviate that nonsense like this
``` rust
#[derive(Component)]
#[require(
    LockedAxes = ROTATION_LOCKED),
)]
pub struct CharacterController;
```
it also works for enum labels.
I chose to omit the type, since were trying to reduce typing here. The
alternative would have been this:
```rust
#[require(
    LockedAxes = LockedAxes::ROTATION_LOCKED),
)]
```
This of course has its disadvantages, since the const has to be
associated, but the old closure method is still possible, so I dont
think its a problem.
- Fixes #16720

## Testing

I added one new test in the docs, which also explain the new change. I
also saw that the docs for the required components on line 165 was
missing an assertion, so I added it back in

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
Wuketuke 2025-03-21 00:02:10 +00:00 committed by François Mockers
parent 1e603558e6
commit baca78b565
2 changed files with 38 additions and 0 deletions

View File

@ -611,6 +611,12 @@ impl Parse for Require {
let func = content.parse::<Path>()?;
Some(RequireFunc::Path(func))
}
} else if input.peek(Token![=]) {
let _t: syn::Token![=] = input.parse()?;
let label: Ident = input.parse()?;
let tokens: TokenStream = quote::quote! (|| #path::#label).into();
let func = syn::parse(tokens).unwrap();
Some(RequireFunc::Closure(func))
} else {
None
};

View File

@ -180,11 +180,43 @@ use thiserror::Error;
/// }
///
/// # let mut world = World::default();
/// // This will implicitly also insert C with the init_c() constructor
/// let id = world.spawn(A).id();
/// assert_eq!(&C(10), world.entity(id).get::<C>().unwrap());
///
/// // This will implicitly also insert C with the `|| C(20)` constructor closure
/// let id = world.spawn(B).id();
/// assert_eq!(&C(20), world.entity(id).get::<C>().unwrap());
/// ```
///
/// For convenience sake, you can abbreviate enum labels or constant values, with the type inferred to match that of the component you are requiring:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// #[require(B = One, C = ONE)]
/// struct A;
///
/// #[derive(Component, PartialEq, Eq, Debug)]
/// enum B {
/// Zero,
/// One,
/// Two
/// }
///
/// #[derive(Component, PartialEq, Eq, Debug)]
/// struct C(u8);
///
/// impl C {
/// pub const ONE: Self = Self(1);
/// }
///
/// # let mut world = World::default();
/// let id = world.spawn(A).id();
/// assert_eq!(&B::One, world.entity(id).get::<B>().unwrap());
/// assert_eq!(&C(1), world.entity(id).get::<C>().unwrap());
/// ````
///
/// Required components are _recursive_. This means, if a Required Component has required components,
/// those components will _also_ be inserted if they are missing:
///