Make Query::single (and friends) return a Result (#18082)
# Objective As discussed in #14275, Bevy is currently too prone to panic, and makes the easy / beginner-friendly way to do a large number of operations just to panic on failure. This is seriously frustrating in library code, but also slows down development, as many of the `Query::single` panics can actually safely be an early return (these panics are often due to a small ordering issue or a change in game state. More critically, in most "finished" products, panics are unacceptable: any unexpected failures should be handled elsewhere. That's where the new With the advent of good system error handling, we can now remove this. Note: I was instrumental in a) introducing this idea in the first place and b) pushing to make the panicking variant the default. The introduction of both `let else` statements in Rust and the fancy system error handling work in 0.16 have changed my mind on the right balance here. ## Solution 1. Make `Query::single` and `Query::single_mut` (and other random related methods) return a `Result`. 2. Handle all of Bevy's internal usage of these APIs. 3. Deprecate `Query::get_single` and friends, since we've moved their functionality to the nice names. 4. Add detailed advice on how to best handle these errors. Generally I like the diff here, although `get_single().unwrap()` in tests is a bit of a downgrade. ## Testing I've done a global search for `.single` to track down any missed deprecated usages. As to whether or not all the migrations were successful, that's what CI is for :) ## Future work ~~Rename `Query::get_single` and friends to `Query::single`!~~ ~~I've opted not to do this in this PR, and smear it across two releases in order to ease the migration. Successive deprecations are much easier to manage than the semantics and types shifting under your feet.~~ Cart has convinced me to change my mind on this; see https://github.com/bevyengine/bevy/pull/18082#discussion_r1974536085. ## Migration guide `Query::single`, `Query::single_mut` and their `QueryState` equivalents now return a `Result`. Generally, you'll want to: 1. Use Bevy 0.16's system error handling to return a `Result` using the `?` operator. 2. Use a `let else Ok(data)` block to early return if it's an expected failure. 3. Use `unwrap()` or `Ok` destructuring inside of tests. The old `Query::get_single` (etc) methods which did this have been deprecated.
This commit is contained in:
parent
ff1ae62e1c
commit
2ad5908e58
@ -162,7 +162,7 @@ fn configure_depth_texture_usages(
|
||||
}
|
||||
|
||||
// Find all the render target that potentially uses OIT
|
||||
let primary_window = p.get_single().ok();
|
||||
let primary_window = p.single().ok();
|
||||
let mut render_target_has_oit = <HashSet<_>>::default();
|
||||
for (camera, has_oit) in &cameras {
|
||||
if has_oit {
|
||||
|
@ -260,7 +260,7 @@ pub fn debug_draw(
|
||||
.map(|(entity, camera)| {
|
||||
(
|
||||
entity,
|
||||
camera.target.normalize(primary_window.get_single().ok()),
|
||||
camera.target.normalize(primary_window.single().ok()),
|
||||
)
|
||||
})
|
||||
.filter_map(|(entity, target)| Some(entity).zip(target))
|
||||
|
@ -27,29 +27,29 @@ fn main() {
|
||||
}
|
||||
|
||||
{
|
||||
let data: &Foo = query.single();
|
||||
let mut data2: Mut<Foo> = query.single_mut();
|
||||
let data: &Foo = query.single().unwrap();
|
||||
let mut data2: Mut<Foo> = query.single_mut().unwrap();
|
||||
//~^ E0502
|
||||
assert_eq!(data, &mut *data2); // oops UB
|
||||
}
|
||||
|
||||
{
|
||||
let mut data2: Mut<Foo> = query.single_mut();
|
||||
let data: &Foo = query.single();
|
||||
let mut data2: Mut<Foo> = query.single_mut().unwrap();
|
||||
let data: &Foo = query.single().unwrap();
|
||||
//~^ E0502
|
||||
assert_eq!(data, &mut *data2); // oops UB
|
||||
}
|
||||
|
||||
{
|
||||
let data: &Foo = query.get_single().unwrap();
|
||||
let mut data2: Mut<Foo> = query.get_single_mut().unwrap();
|
||||
let data: &Foo = query.single().unwrap();
|
||||
let mut data2: Mut<Foo> = query.single_mut().unwrap();
|
||||
//~^ E0502
|
||||
assert_eq!(data, &mut *data2); // oops UB
|
||||
}
|
||||
|
||||
{
|
||||
let mut data2: Mut<Foo> = query.get_single_mut().unwrap();
|
||||
let data: &Foo = query.get_single().unwrap();
|
||||
let mut data2: Mut<Foo> = query.single_mut().unwrap();
|
||||
let data: &Foo = query.single().unwrap();
|
||||
//~^ E0502
|
||||
assert_eq!(data, &mut *data2); // oops UB
|
||||
}
|
||||
|
@ -45,9 +45,9 @@ error[E0502]: cannot borrow `query` as immutable because it is also borrowed as
|
||||
error[E0502]: cannot borrow `query` as mutable because it is also borrowed as immutable
|
||||
--> tests/ui/query_lifetime_safety.rs:45:39
|
||||
|
|
||||
44 | let data: &Foo = query.get_single().unwrap();
|
||||
44 | let data: &Foo = query.single().unwrap();
|
||||
| ----- immutable borrow occurs here
|
||||
45 | let mut data2: Mut<Foo> = query.get_single_mut().unwrap();
|
||||
45 | let mut data2: Mut<Foo> = query.single_mut().unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
|
||||
46 |
|
||||
47 | assert_eq!(data, &mut *data2); // oops UB
|
||||
@ -56,9 +56,9 @@ error[E0502]: cannot borrow `query` as mutable because it is also borrowed as im
|
||||
error[E0502]: cannot borrow `query` as immutable because it is also borrowed as mutable
|
||||
--> tests/ui/query_lifetime_safety.rs:52:30
|
||||
|
|
||||
51 | let mut data2: Mut<Foo> = query.get_single_mut().unwrap();
|
||||
51 | let mut data2: Mut<Foo> = query.single_mut().unwrap();
|
||||
| ----- mutable borrow occurs here
|
||||
52 | let data: &Foo = query.get_single().unwrap();
|
||||
52 | let data: &Foo = query.single().unwrap();
|
||||
| ^^^^^ immutable borrow occurs here
|
||||
53 |
|
||||
54 | assert_eq!(data, &mut *data2); // oops UB
|
||||
|
@ -35,13 +35,13 @@ fn for_loops(mut query: Query<&mut Foo>) {
|
||||
fn single_mut_query(mut query: Query<&mut Foo>) {
|
||||
// this should fail to compile
|
||||
{
|
||||
let mut mut_foo = query.single_mut();
|
||||
let mut mut_foo = query.single_mut().unwrap();
|
||||
|
||||
// This solves "temporary value dropped while borrowed"
|
||||
let readonly_query = query.as_readonly();
|
||||
//~^ E0502
|
||||
|
||||
let ref_foo = readonly_query.single();
|
||||
let ref_foo = readonly_query.single().unwrap();
|
||||
|
||||
*mut_foo = Foo;
|
||||
|
||||
@ -55,7 +55,7 @@ fn single_mut_query(mut query: Query<&mut Foo>) {
|
||||
|
||||
let ref_foo = readonly_query.single();
|
||||
|
||||
let mut mut_foo = query.single_mut();
|
||||
let mut mut_foo = query.single_mut().unwrap();
|
||||
//~^ E0502
|
||||
|
||||
println!("{ref_foo:?}");
|
||||
|
@ -22,8 +22,8 @@ fn main() {
|
||||
let mut query_a = lens_a.query();
|
||||
let mut query_b = lens_b.query();
|
||||
|
||||
let a = query_a.single_mut();
|
||||
let b = query_b.single_mut(); // oops 2 mutable references to same Foo
|
||||
let a = query_a.single_mut().unwrap();
|
||||
let b = query_b.single_mut().unwrap(); // oops 2 mutable references to same Foo
|
||||
assert_eq!(*a, *b);
|
||||
}
|
||||
|
||||
@ -34,8 +34,8 @@ fn main() {
|
||||
let mut query_b = lens.query();
|
||||
//~^ E0499
|
||||
|
||||
let a = query_a.single_mut();
|
||||
let b = query_b.single_mut(); // oops 2 mutable references to same Foo
|
||||
let a = query_a.single_mut().unwrap();
|
||||
let b = query_b.single_mut().unwrap(); // oops 2 mutable references to same Foo
|
||||
assert_eq!(*a, *b);
|
||||
}
|
||||
}
|
||||
|
@ -1557,7 +1557,7 @@ mod tests {
|
||||
// Since the world is always ahead, as long as changes can't get older than `u32::MAX` (which we ensure),
|
||||
// the wrapping difference will always be positive, so wraparound doesn't matter.
|
||||
let mut query = world.query::<Ref<C>>();
|
||||
assert!(query.single(&world).is_changed());
|
||||
assert!(query.single(&world).unwrap().is_changed());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -33,7 +33,7 @@ use super::{FilteredAccess, QueryData, QueryFilter};
|
||||
/// .build();
|
||||
///
|
||||
/// // Consume the QueryState
|
||||
/// let (entity, b) = query.single(&world);
|
||||
/// let (entity, b) = query.single(&world).unwrap();
|
||||
/// ```
|
||||
pub struct QueryBuilder<'w, D: QueryData = (), F: QueryFilter = ()> {
|
||||
access: FilteredAccess<ComponentId>,
|
||||
@ -297,13 +297,13 @@ mod tests {
|
||||
.with::<A>()
|
||||
.without::<C>()
|
||||
.build();
|
||||
assert_eq!(entity_a, query_a.single(&world));
|
||||
assert_eq!(entity_a, query_a.single(&world).unwrap());
|
||||
|
||||
let mut query_b = QueryBuilder::<Entity>::new(&mut world)
|
||||
.with::<A>()
|
||||
.without::<B>()
|
||||
.build();
|
||||
assert_eq!(entity_b, query_b.single(&world));
|
||||
assert_eq!(entity_b, query_b.single(&world).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -319,13 +319,13 @@ mod tests {
|
||||
.with_id(component_id_a)
|
||||
.without_id(component_id_c)
|
||||
.build();
|
||||
assert_eq!(entity_a, query_a.single(&world));
|
||||
assert_eq!(entity_a, query_a.single(&world).unwrap());
|
||||
|
||||
let mut query_b = QueryBuilder::<Entity>::new(&mut world)
|
||||
.with_id(component_id_a)
|
||||
.without_id(component_id_b)
|
||||
.build();
|
||||
assert_eq!(entity_b, query_b.single(&world));
|
||||
assert_eq!(entity_b, query_b.single(&world).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -385,7 +385,7 @@ mod tests {
|
||||
.data::<&B>()
|
||||
.build();
|
||||
|
||||
let entity_ref = query.single(&world);
|
||||
let entity_ref = query.single(&world).unwrap();
|
||||
|
||||
assert_eq!(entity, entity_ref.id());
|
||||
|
||||
@ -408,7 +408,7 @@ mod tests {
|
||||
.ref_id(component_id_b)
|
||||
.build();
|
||||
|
||||
let entity_ref = query.single(&world);
|
||||
let entity_ref = query.single(&world).unwrap();
|
||||
|
||||
assert_eq!(entity, entity_ref.id());
|
||||
|
||||
|
@ -104,7 +104,7 @@ impl<'w> PartialEq for QueryEntityError<'w> {
|
||||
impl<'w> Eq for QueryEntityError<'w> {}
|
||||
|
||||
/// An error that occurs when evaluating a [`Query`](crate::system::Query) or [`QueryState`](crate::query::QueryState) as a single expected result via
|
||||
/// [`get_single`](crate::system::Query::get_single) or [`get_single_mut`](crate::system::Query::get_single_mut).
|
||||
/// [`single`](crate::system::Query::single) or [`single_mut`](crate::system::Query::single_mut).
|
||||
#[derive(Debug, Error)]
|
||||
pub enum QuerySingleError {
|
||||
/// No entity fits the query.
|
||||
|
@ -763,8 +763,8 @@ mod tests {
|
||||
let _: Option<&Foo> = q.get(&world, e).ok();
|
||||
let _: Option<&Foo> = q.get_manual(&world, e).ok();
|
||||
let _: Option<[&Foo; 1]> = q.get_many(&world, [e]).ok();
|
||||
let _: Option<&Foo> = q.get_single(&world).ok();
|
||||
let _: &Foo = q.single(&world);
|
||||
let _: Option<&Foo> = q.single(&world).ok();
|
||||
let _: &Foo = q.single(&world).unwrap();
|
||||
|
||||
// system param
|
||||
let mut q = SystemState::<Query<&mut Foo>>::new(&mut world);
|
||||
@ -776,9 +776,9 @@ mod tests {
|
||||
|
||||
let _: Option<&Foo> = q.get(e).ok();
|
||||
let _: Option<[&Foo; 1]> = q.get_many([e]).ok();
|
||||
let _: Option<&Foo> = q.get_single().ok();
|
||||
let _: Option<&Foo> = q.single().ok();
|
||||
let _: [&Foo; 1] = q.many([e]);
|
||||
let _: &Foo = q.single();
|
||||
let _: &Foo = q.single().unwrap();
|
||||
}
|
||||
|
||||
// regression test for https://github.com/bevyengine/bevy/pull/8029
|
||||
|
@ -1597,43 +1597,88 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
|
||||
/// This can only be called for read-only queries,
|
||||
/// see [`single_mut`](Self::single_mut) for write-queries.
|
||||
///
|
||||
/// # Panics
|
||||
/// If the number of query results is not exactly one, a [`QuerySingleError`] is returned
|
||||
/// instead.
|
||||
///
|
||||
/// Panics if the number of query results is not exactly one. Use
|
||||
/// [`get_single`](Self::get_single) to return a `Result` instead of panicking.
|
||||
#[track_caller]
|
||||
/// # Example
|
||||
///
|
||||
/// Sometimes, you might want to handle the error in a specific way,
|
||||
/// generally by spawning the missing entity.
|
||||
///
|
||||
/// ```rust
|
||||
/// use bevy_ecs::prelude::*;
|
||||
/// use bevy_ecs::query::QuerySingleError;
|
||||
///
|
||||
/// #[derive(Component)]
|
||||
/// struct A(usize);
|
||||
///
|
||||
/// fn my_system(query: Query<&A>, mut commands: Commands) {
|
||||
/// match query.single() {
|
||||
/// Ok(a) => (), // Do something with `a`
|
||||
/// Err(err) => match err {
|
||||
/// QuerySingleError::NoEntities(_) => {
|
||||
/// commands.spawn(A(0));
|
||||
/// }
|
||||
/// QuerySingleError::MultipleEntities(_) => panic!("Multiple entities found!"),
|
||||
/// },
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// However in most cases, this error can simply be handled with a graceful early return.
|
||||
/// If this is an expected failure mode, you can do this using the `let else` pattern like so:
|
||||
/// ```rust
|
||||
/// use bevy_ecs::prelude::*;
|
||||
///
|
||||
/// #[derive(Component)]
|
||||
/// struct A(usize);
|
||||
///
|
||||
/// fn my_system(query: Query<&A>) {
|
||||
/// let Ok(a) = query.single() else {
|
||||
/// return;
|
||||
/// };
|
||||
///
|
||||
/// // Do something with `a`
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// If this is unexpected though, you should probably use the `?` operator
|
||||
/// in combination with Bevy's error handling apparatus.
|
||||
///
|
||||
/// ```rust
|
||||
/// use bevy_ecs::prelude::*;
|
||||
///
|
||||
/// #[derive(Component)]
|
||||
/// struct A(usize);
|
||||
///
|
||||
/// fn my_system(query: Query<&A>) -> Result {
|
||||
/// let a = query.single()?;
|
||||
///
|
||||
/// // Do something with `a`
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This allows you to globally control how errors are handled in your application,
|
||||
/// by setting up a custom error handler.
|
||||
/// See the [`bevy_ecs::result`] module docs for more information!
|
||||
/// Commonly, you might want to panic on an error during development, but log the error and continue
|
||||
/// execution in production.
|
||||
///
|
||||
/// Simply unwrapping the [`Result`] also works, but should generally be reserved for tests.
|
||||
#[inline]
|
||||
pub fn single<'w>(&mut self, world: &'w World) -> ROQueryItem<'w, D> {
|
||||
pub fn single<'w>(&mut self, world: &'w World) -> Result<ROQueryItem<'w, D>, QuerySingleError> {
|
||||
self.query(world).single_inner()
|
||||
}
|
||||
|
||||
/// Returns a single immutable query result when there is exactly one entity matching
|
||||
/// the query.
|
||||
///
|
||||
/// This can only be called for read-only queries,
|
||||
/// see [`get_single_mut`](Self::get_single_mut) for write-queries.
|
||||
///
|
||||
/// If the number of query results is not exactly one, a [`QuerySingleError`] is returned
|
||||
/// instead.
|
||||
/// A deprecated alias for [`QueryState::single`].
|
||||
#[deprecated(since = "0.16.0", note = "Please use `single` instead.")]
|
||||
#[inline]
|
||||
pub fn get_single<'w>(
|
||||
&mut self,
|
||||
world: &'w World,
|
||||
) -> Result<ROQueryItem<'w, D>, QuerySingleError> {
|
||||
self.query(world).get_single_inner()
|
||||
}
|
||||
|
||||
/// Returns a single mutable query result when there is exactly one entity matching
|
||||
/// the query.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the number of query results is not exactly one. Use
|
||||
/// [`get_single_mut`](Self::get_single_mut) to return a `Result` instead of panicking.
|
||||
#[track_caller]
|
||||
#[inline]
|
||||
pub fn single_mut<'w>(&mut self, world: &'w mut World) -> D::Item<'w> {
|
||||
self.query_mut(world).single_inner()
|
||||
self.single(world)
|
||||
}
|
||||
|
||||
/// Returns a single mutable query result when there is exactly one entity matching
|
||||
@ -1641,12 +1686,25 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
|
||||
///
|
||||
/// If the number of query results is not exactly one, a [`QuerySingleError`] is returned
|
||||
/// instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Please see [`Query::single`] for advice on handling the error.
|
||||
#[inline]
|
||||
pub fn single_mut<'w>(
|
||||
&mut self,
|
||||
world: &'w mut World,
|
||||
) -> Result<D::Item<'w>, QuerySingleError> {
|
||||
self.query_mut(world).single_inner()
|
||||
}
|
||||
|
||||
/// A deprecated alias for [`QueryState::single_mut`].
|
||||
#[deprecated(since = "0.16.0", note = "Please use `single` instead.")]
|
||||
pub fn get_single_mut<'w>(
|
||||
&mut self,
|
||||
world: &'w mut World,
|
||||
) -> Result<D::Item<'w>, QuerySingleError> {
|
||||
self.query_mut(world).get_single_inner()
|
||||
self.single_mut(world)
|
||||
}
|
||||
|
||||
/// Returns a query result when there is exactly one entity matching the query.
|
||||
@ -1659,11 +1717,11 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
|
||||
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
|
||||
/// have unique access to the components they query.
|
||||
#[inline]
|
||||
pub unsafe fn get_single_unchecked<'w>(
|
||||
pub unsafe fn single_unchecked<'w>(
|
||||
&mut self,
|
||||
world: UnsafeWorldCell<'w>,
|
||||
) -> Result<D::Item<'w>, QuerySingleError> {
|
||||
self.query_unchecked(world).get_single_inner()
|
||||
self.query_unchecked(world).single_inner()
|
||||
}
|
||||
|
||||
/// Returns a query result when there is exactly one entity matching the query,
|
||||
@ -1679,7 +1737,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
|
||||
/// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world`
|
||||
/// with a mismatched [`WorldId`] is unsound.
|
||||
#[inline]
|
||||
pub unsafe fn get_single_unchecked_manual<'w>(
|
||||
pub unsafe fn single_unchecked_manual<'w>(
|
||||
&self,
|
||||
world: UnsafeWorldCell<'w>,
|
||||
last_run: Tick,
|
||||
@ -1689,7 +1747,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
|
||||
// - The caller ensured we have the correct access to the world.
|
||||
// - The caller ensured that the world matches.
|
||||
self.query_unchecked_manual_with_ticks(world, last_run, this_run)
|
||||
.get_single_inner()
|
||||
.single_inner()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1753,7 +1811,7 @@ mod tests {
|
||||
let query_state = world.query::<(&A, &B)>();
|
||||
let mut new_query_state = query_state.transmute::<&A>(&world);
|
||||
assert_eq!(new_query_state.iter(&world).len(), 1);
|
||||
let a = new_query_state.single(&world);
|
||||
let a = new_query_state.single(&world).unwrap();
|
||||
|
||||
assert_eq!(a.0, 1);
|
||||
}
|
||||
@ -1767,7 +1825,7 @@ mod tests {
|
||||
let query_state = world.query_filtered::<(&A, &B), Without<C>>();
|
||||
let mut new_query_state = query_state.transmute::<&A>(&world);
|
||||
// even though we change the query to not have Without<C>, we do not get the component with C.
|
||||
let a = new_query_state.single(&world);
|
||||
let a = new_query_state.single(&world).unwrap();
|
||||
|
||||
assert_eq!(a.0, 0);
|
||||
}
|
||||
@ -1780,7 +1838,7 @@ mod tests {
|
||||
|
||||
let q = world.query::<()>();
|
||||
let mut q = q.transmute::<Entity>(&world);
|
||||
assert_eq!(q.single(&world), entity);
|
||||
assert_eq!(q.single(&world).unwrap(), entity);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1790,7 +1848,7 @@ mod tests {
|
||||
|
||||
let q = world.query::<&A>();
|
||||
let mut new_q = q.transmute::<Ref<A>>(&world);
|
||||
assert!(new_q.single(&world).is_added());
|
||||
assert!(new_q.single(&world).unwrap().is_added());
|
||||
|
||||
let q = world.query::<Ref<A>>();
|
||||
let _ = q.transmute::<&A>(&world);
|
||||
@ -1861,7 +1919,7 @@ mod tests {
|
||||
|
||||
let query_state = world.query::<Option<&A>>();
|
||||
let mut new_query_state = query_state.transmute::<&A>(&world);
|
||||
let x = new_query_state.single(&world);
|
||||
let x = new_query_state.single(&world).unwrap();
|
||||
assert_eq!(x.0, 1234);
|
||||
}
|
||||
|
||||
@ -1886,7 +1944,7 @@ mod tests {
|
||||
|
||||
let mut query = query;
|
||||
// Our result is completely untyped
|
||||
let entity_ref = query.single(&world);
|
||||
let entity_ref = query.single(&world).unwrap();
|
||||
|
||||
assert_eq!(entity, entity_ref.id());
|
||||
assert_eq!(0, entity_ref.get::<A>().unwrap().0);
|
||||
@ -1901,16 +1959,16 @@ mod tests {
|
||||
let mut query = QueryState::<(Entity, &A, Has<B>)>::new(&mut world)
|
||||
.transmute_filtered::<(Entity, Has<B>), Added<A>>(&world);
|
||||
|
||||
assert_eq!((entity_a, false), query.single(&world));
|
||||
assert_eq!((entity_a, false), query.single(&world).unwrap());
|
||||
|
||||
world.clear_trackers();
|
||||
|
||||
let entity_b = world.spawn((A(0), B(0))).id();
|
||||
assert_eq!((entity_b, true), query.single(&world));
|
||||
assert_eq!((entity_b, true), query.single(&world).unwrap());
|
||||
|
||||
world.clear_trackers();
|
||||
|
||||
assert!(query.get_single(&world).is_err());
|
||||
assert!(query.single(&world).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1922,15 +1980,15 @@ mod tests {
|
||||
.transmute_filtered::<Entity, Changed<A>>(&world);
|
||||
|
||||
let mut change_query = QueryState::<&mut A>::new(&mut world);
|
||||
assert_eq!(entity_a, detection_query.single(&world));
|
||||
assert_eq!(entity_a, detection_query.single(&world).unwrap());
|
||||
|
||||
world.clear_trackers();
|
||||
|
||||
assert!(detection_query.get_single(&world).is_err());
|
||||
assert!(detection_query.single(&world).is_err());
|
||||
|
||||
change_query.single_mut(&mut world).0 = 1;
|
||||
change_query.single_mut(&mut world).unwrap().0 = 1;
|
||||
|
||||
assert_eq!(entity_a, detection_query.single(&world));
|
||||
assert_eq!(entity_a, detection_query.single(&world).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -2017,7 +2075,7 @@ mod tests {
|
||||
let query_2 = QueryState::<&B, Without<C>>::new(&mut world);
|
||||
let mut new_query: QueryState<Entity, ()> = query_1.join_filtered(&world, &query_2);
|
||||
|
||||
assert_eq!(new_query.single(&world), entity_ab);
|
||||
assert_eq!(new_query.single(&world).unwrap(), entity_ab);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -270,7 +270,7 @@ use core::{
|
||||
/// |[`iter_combinations`]\[[`_mut`][`iter_combinations_mut`]]|Returns an iterator over all combinations of a specified number of query items.|
|
||||
/// |[`get`]\[[`_mut`][`get_mut`]]|Returns the query item for the specified entity.|
|
||||
/// |[`many`]\[[`_mut`][`many_mut`]],<br>[`get_many`]\[[`_mut`][`get_many_mut`]]|Returns the query items for the specified entities.|
|
||||
/// |[`single`]\[[`_mut`][`single_mut`]],<br>[`get_single`]\[[`_mut`][`get_single_mut`]]|Returns the query item while verifying that there aren't others.|
|
||||
/// |[`single`]\[[`_mut`][`single_mut`]],<br>[`single`]\[[`_mut`][`single_mut`]]|Returns the query item while verifying that there aren't others.|
|
||||
///
|
||||
/// There are two methods for each type of query operation: immutable and mutable (ending with `_mut`).
|
||||
/// When using immutable methods, the query items returned are of type [`ROQueryItem`], a read-only version of the query item.
|
||||
@ -307,7 +307,7 @@ use core::{
|
||||
/// |[`get`]\[[`_mut`][`get_mut`]]|O(1)|
|
||||
/// |([`get_`][`get_many`])[`many`]|O(k)|
|
||||
/// |([`get_`][`get_many_mut`])[`many_mut`]|O(k<sup>2</sup>)|
|
||||
/// |[`single`]\[[`_mut`][`single_mut`]],<br>[`get_single`]\[[`_mut`][`get_single_mut`]]|O(a)|
|
||||
/// |[`single`]\[[`_mut`][`single_mut`]],<br>[`single`]\[[`_mut`][`single_mut`]]|O(a)|
|
||||
/// |Archetype based filtering ([`With`], [`Without`], [`Or`])|O(a)|
|
||||
/// |Change detection filtering ([`Added`], [`Changed`])|O(a + n)|
|
||||
///
|
||||
@ -351,8 +351,8 @@ use core::{
|
||||
/// [`get_many`]: Self::get_many
|
||||
/// [`get_many_mut`]: Self::get_many_mut
|
||||
/// [`get_mut`]: Self::get_mut
|
||||
/// [`get_single`]: Self::get_single
|
||||
/// [`get_single_mut`]: Self::get_single_mut
|
||||
/// [`single`]: Self::single
|
||||
/// [`single_mut`]: Self::single_mut
|
||||
/// [`iter`]: Self::iter
|
||||
/// [`iter_combinations`]: Self::iter_combinations
|
||||
/// [`iter_combinations_mut`]: Self::iter_combinations_mut
|
||||
@ -947,7 +947,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
||||
/// friends_query: Query<&Friends>,
|
||||
/// mut counter_query: Query<&mut Counter>,
|
||||
/// ) {
|
||||
/// let friends = friends_query.single();
|
||||
/// let friends = friends_query.single().unwrap();
|
||||
/// for mut counter in counter_query.iter_many_unique_inner(friends) {
|
||||
/// println!("Friend's counter: {:?}", counter.value);
|
||||
/// counter.value += 1;
|
||||
@ -1706,36 +1706,6 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
||||
unsafe { self.reborrow_unsafe() }.get_inner(entity)
|
||||
}
|
||||
|
||||
/// Returns a single read-only query item when there is exactly one entity matching the query.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method panics if the number of query items is **not** exactly one.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// # #[derive(Component)]
|
||||
/// # struct Player;
|
||||
/// # #[derive(Component)]
|
||||
/// # struct Position(f32, f32);
|
||||
/// fn player_system(query: Query<&Position, With<Player>>) {
|
||||
/// let player_position = query.single();
|
||||
/// // do something with player_position
|
||||
/// }
|
||||
/// # bevy_ecs::system::assert_is_system(player_system);
|
||||
/// ```
|
||||
///
|
||||
/// # See also
|
||||
///
|
||||
/// - [`get_single`](Self::get_single) for the non-panicking version.
|
||||
/// - [`single_mut`](Self::single_mut) to get the mutable query item.
|
||||
#[track_caller]
|
||||
pub fn single(&self) -> ROQueryItem<'_, D> {
|
||||
self.get_single().unwrap()
|
||||
}
|
||||
|
||||
/// Returns a single read-only query item when there is exactly one entity matching the query.
|
||||
///
|
||||
/// If the number of query items is not exactly one, a [`QuerySingleError`] is returned instead.
|
||||
@ -1748,7 +1718,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
||||
/// # #[derive(Component)]
|
||||
/// # struct PlayerScore(i32);
|
||||
/// fn player_scoring_system(query: Query<&PlayerScore>) {
|
||||
/// match query.get_single() {
|
||||
/// match query.single() {
|
||||
/// Ok(PlayerScore(score)) => {
|
||||
/// println!("Score: {}", score);
|
||||
/// }
|
||||
@ -1765,43 +1735,16 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
||||
///
|
||||
/// # See also
|
||||
///
|
||||
/// - [`get_single_mut`](Self::get_single_mut) to get the mutable query item.
|
||||
/// - [`single`](Self::single) for the panicking version.
|
||||
/// - [`single_mut`](Self::single_mut) to get the mutable query item.
|
||||
#[inline]
|
||||
pub fn get_single(&self) -> Result<ROQueryItem<'_, D>, QuerySingleError> {
|
||||
self.as_readonly().get_single_inner()
|
||||
pub fn single(&self) -> Result<ROQueryItem<'_, D>, QuerySingleError> {
|
||||
self.as_readonly().single_inner()
|
||||
}
|
||||
|
||||
/// Returns a single query item when there is exactly one entity matching the query.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method panics if the number of query items is **not** exactly one.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// #
|
||||
/// # #[derive(Component)]
|
||||
/// # struct Player;
|
||||
/// # #[derive(Component)]
|
||||
/// # struct Health(u32);
|
||||
/// #
|
||||
/// fn regenerate_player_health_system(mut query: Query<&mut Health, With<Player>>) {
|
||||
/// let mut health = query.single_mut();
|
||||
/// health.0 += 1;
|
||||
/// }
|
||||
/// # bevy_ecs::system::assert_is_system(regenerate_player_health_system);
|
||||
/// ```
|
||||
///
|
||||
/// # See also
|
||||
///
|
||||
/// - [`get_single_mut`](Self::get_single_mut) for the non-panicking version.
|
||||
/// - [`single`](Self::single) to get the read-only query item.
|
||||
#[track_caller]
|
||||
pub fn single_mut(&mut self) -> D::Item<'_> {
|
||||
self.get_single_mut().unwrap()
|
||||
/// A deprecated alias for [`single`](Self::single).
|
||||
#[deprecated(note = "Please use `single` instead")]
|
||||
pub fn get_single(&self) -> Result<ROQueryItem<'_, D>, QuerySingleError> {
|
||||
self.single()
|
||||
}
|
||||
|
||||
/// Returns a single query item when there is exactly one entity matching the query.
|
||||
@ -1819,7 +1762,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
||||
/// # struct Health(u32);
|
||||
/// #
|
||||
/// fn regenerate_player_health_system(mut query: Query<&mut Health, With<Player>>) {
|
||||
/// let mut health = query.get_single_mut().expect("Error: Could not find a single player.");
|
||||
/// let mut health = query.single_mut().expect("Error: Could not find a single player.");
|
||||
/// health.0 += 1;
|
||||
/// }
|
||||
/// # bevy_ecs::system::assert_is_system(regenerate_player_health_system);
|
||||
@ -1827,19 +1770,22 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
||||
///
|
||||
/// # See also
|
||||
///
|
||||
/// - [`get_single`](Self::get_single) to get the read-only query item.
|
||||
/// - [`single_mut`](Self::single_mut) for the panicking version.
|
||||
/// - [`single`](Self::single) to get the read-only query item.
|
||||
#[inline]
|
||||
pub fn single_mut(&mut self) -> Result<D::Item<'_>, QuerySingleError> {
|
||||
self.reborrow().single_inner()
|
||||
}
|
||||
|
||||
/// A deprecated alias for [`single_mut`](Self::single_mut).
|
||||
#[deprecated(note = "Please use `single_mut` instead")]
|
||||
pub fn get_single_mut(&mut self) -> Result<D::Item<'_>, QuerySingleError> {
|
||||
self.reborrow().get_single_inner()
|
||||
self.single_mut()
|
||||
}
|
||||
|
||||
/// Returns a single query item when there is exactly one entity matching the query.
|
||||
/// This consumes the [`Query`] to return results with the actual "inner" world lifetime.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method panics if the number of query items is **not** exactly one.
|
||||
/// If the number of query items is not exactly one, a [`QuerySingleError`] is returned instead.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -1852,7 +1798,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
||||
/// # struct Health(u32);
|
||||
/// #
|
||||
/// fn regenerate_player_health_system(query: Query<&mut Health, With<Player>>) {
|
||||
/// let mut health = query.single_inner();
|
||||
/// let mut health = query.single_inner().expect("Error: Could not find a single player.");
|
||||
/// health.0 += 1;
|
||||
/// }
|
||||
/// # bevy_ecs::system::assert_is_system(regenerate_player_health_system);
|
||||
@ -1860,43 +1806,11 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
||||
///
|
||||
/// # See also
|
||||
///
|
||||
/// - [`get_single_inner`](Self::get_single_inner) for the non-panicking version.
|
||||
/// - [`single`](Self::single) to get the read-only query item.
|
||||
/// - [`single_mut`](Self::single_mut) to get the mutable query item.
|
||||
#[track_caller]
|
||||
pub fn single_inner(self) -> D::Item<'w> {
|
||||
self.get_single_inner().unwrap()
|
||||
}
|
||||
|
||||
/// Returns a single query item when there is exactly one entity matching the query.
|
||||
/// This consumes the [`Query`] to return results with the actual "inner" world lifetime.
|
||||
///
|
||||
/// If the number of query items is not exactly one, a [`QuerySingleError`] is returned instead.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// #
|
||||
/// # #[derive(Component)]
|
||||
/// # struct Player;
|
||||
/// # #[derive(Component)]
|
||||
/// # struct Health(u32);
|
||||
/// #
|
||||
/// fn regenerate_player_health_system(query: Query<&mut Health, With<Player>>) {
|
||||
/// let mut health = query.get_single_inner().expect("Error: Could not find a single player.");
|
||||
/// health.0 += 1;
|
||||
/// }
|
||||
/// # bevy_ecs::system::assert_is_system(regenerate_player_health_system);
|
||||
/// ```
|
||||
///
|
||||
/// # See also
|
||||
///
|
||||
/// - [`get_single`](Self::get_single) to get the read-only query item.
|
||||
/// - [`get_single_mut`](Self::get_single_mut) to get the mutable query item.
|
||||
/// - [`single_inner`](Self::single_inner) for the panicking version.
|
||||
#[inline]
|
||||
pub fn get_single_inner(self) -> Result<D::Item<'w>, QuerySingleError> {
|
||||
pub fn single_inner(self) -> Result<D::Item<'w>, QuerySingleError> {
|
||||
let mut query = self.into_iter();
|
||||
let first = query.next();
|
||||
let extra = query.next().is_some();
|
||||
@ -2001,7 +1915,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
||||
/// # world.spawn((A(10), B(5)));
|
||||
/// #
|
||||
/// fn reusable_function(lens: &mut QueryLens<&A>) {
|
||||
/// assert_eq!(lens.query().single().0, 10);
|
||||
/// assert_eq!(lens.query().single().unwrap().0, 10);
|
||||
/// }
|
||||
///
|
||||
/// // We can use the function in a system that takes the exact query.
|
||||
@ -2160,7 +2074,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
||||
/// # world.spawn((A(10), B(5)));
|
||||
/// #
|
||||
/// fn reusable_function(mut lens: QueryLens<&A>) {
|
||||
/// assert_eq!(lens.query().single().0, 10);
|
||||
/// assert_eq!(lens.query().single().unwrap().0, 10);
|
||||
/// }
|
||||
///
|
||||
/// // We can use the function in a system that takes the exact query.
|
||||
|
@ -408,7 +408,7 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam fo
|
||||
state.query_unchecked_manual_with_ticks(world, system_meta.last_run, change_tick)
|
||||
};
|
||||
let single = query
|
||||
.get_single_inner()
|
||||
.single_inner()
|
||||
.expect("The query was expected to contain exactly one matching entity.");
|
||||
Single {
|
||||
item: single,
|
||||
@ -432,7 +432,7 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam fo
|
||||
world.change_tick(),
|
||||
)
|
||||
};
|
||||
let is_valid = query.get_single_inner().is_ok();
|
||||
let is_valid = query.single_inner().is_ok();
|
||||
if !is_valid {
|
||||
system_meta.try_warn_param::<Self>();
|
||||
}
|
||||
@ -474,7 +474,7 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam
|
||||
let query = unsafe {
|
||||
state.query_unchecked_manual_with_ticks(world, system_meta.last_run, change_tick)
|
||||
};
|
||||
match query.get_single_inner() {
|
||||
match query.single_inner() {
|
||||
Ok(single) => Some(Single {
|
||||
item: single,
|
||||
_filter: PhantomData,
|
||||
@ -500,7 +500,7 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam
|
||||
world.change_tick(),
|
||||
)
|
||||
};
|
||||
let result = query.get_single_inner();
|
||||
let result = query.single_inner();
|
||||
let is_valid = !matches!(result, Err(QuerySingleError::MultipleEntities(_)));
|
||||
if !is_valid {
|
||||
system_meta.try_warn_param::<Self>();
|
||||
|
@ -2430,11 +2430,11 @@ impl<'w> EntityWorldMut<'w> {
|
||||
/// let mut entity = world.spawn_empty();
|
||||
/// entity.entry().or_insert_with(|| Comp(4));
|
||||
/// # let entity_id = entity.id();
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 4);
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 4);
|
||||
///
|
||||
/// # let mut entity = world.get_entity_mut(entity_id).unwrap();
|
||||
/// entity.entry::<Comp>().and_modify(|mut c| c.0 += 1);
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 5);
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 5);
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
@ -2714,7 +2714,7 @@ impl<'w, 'a, T: Component<Mutability = Mutable>> Entry<'w, 'a, T> {
|
||||
/// let mut entity = world.spawn(Comp(0));
|
||||
///
|
||||
/// entity.entry::<Comp>().and_modify(|mut c| c.0 += 1);
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 1);
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 1);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn and_modify<F: FnOnce(Mut<'_, T>)>(self, f: F) -> Self {
|
||||
@ -2773,11 +2773,11 @@ impl<'w, 'a, T: Component> Entry<'w, 'a, T> {
|
||||
///
|
||||
/// entity.entry().or_insert(Comp(4));
|
||||
/// # let entity_id = entity.id();
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 4);
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 4);
|
||||
///
|
||||
/// # let mut entity = world.get_entity_mut(entity_id).unwrap();
|
||||
/// entity.entry().or_insert(Comp(15)).into_mut().0 *= 2;
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 8);
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 8);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn or_insert(self, default: T) -> OccupiedEntry<'w, 'a, T> {
|
||||
@ -2801,7 +2801,7 @@ impl<'w, 'a, T: Component> Entry<'w, 'a, T> {
|
||||
/// let mut entity = world.spawn_empty();
|
||||
///
|
||||
/// entity.entry().or_insert_with(|| Comp(4));
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 4);
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 4);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn or_insert_with<F: FnOnce() -> T>(self, default: F) -> OccupiedEntry<'w, 'a, T> {
|
||||
@ -2827,7 +2827,7 @@ impl<'w, 'a, T: Component + Default> Entry<'w, 'a, T> {
|
||||
/// let mut entity = world.spawn_empty();
|
||||
///
|
||||
/// entity.entry::<Comp>().or_default();
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 0);
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 0);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn or_default(self) -> OccupiedEntry<'w, 'a, T> {
|
||||
@ -2885,7 +2885,7 @@ impl<'w, 'a, T: Component> OccupiedEntry<'w, 'a, T> {
|
||||
/// o.insert(Comp(10));
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 10);
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 10);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn insert(&mut self, component: T) {
|
||||
@ -2943,7 +2943,7 @@ impl<'w, 'a, T: Component<Mutability = Mutable>> OccupiedEntry<'w, 'a, T> {
|
||||
/// o.get_mut().0 += 2
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 17);
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 17);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn get_mut(&mut self) -> Mut<'_, T> {
|
||||
@ -2972,7 +2972,7 @@ impl<'w, 'a, T: Component<Mutability = Mutable>> OccupiedEntry<'w, 'a, T> {
|
||||
/// o.into_mut().0 += 10;
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 15);
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 15);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn into_mut(self) -> Mut<'a, T> {
|
||||
@ -3004,7 +3004,7 @@ impl<'w, 'a, T: Component> VacantEntry<'w, 'a, T> {
|
||||
/// v.insert(Comp(10));
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).0, 10);
|
||||
/// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 10);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn insert(self, component: T) -> OccupiedEntry<'w, 'a, T> {
|
||||
@ -3036,7 +3036,7 @@ impl<'w, 'a, T: Component> VacantEntry<'w, 'a, T> {
|
||||
/// .data::<&A>()
|
||||
/// .build();
|
||||
///
|
||||
/// let filtered_entity: FilteredEntityRef = query.single(&mut world);
|
||||
/// let filtered_entity: FilteredEntityRef = query.single(&mut world).unwrap();
|
||||
/// let component: &A = filtered_entity.get().unwrap();
|
||||
///
|
||||
/// // Here `FilteredEntityRef` is nested in a tuple, so it does not have access to `&A`.
|
||||
@ -3044,7 +3044,7 @@ impl<'w, 'a, T: Component> VacantEntry<'w, 'a, T> {
|
||||
/// .data::<&A>()
|
||||
/// .build();
|
||||
///
|
||||
/// let (_, filtered_entity) = query.single(&mut world);
|
||||
/// let (_, filtered_entity) = query.single(&mut world).unwrap();
|
||||
/// assert!(filtered_entity.get::<A>().is_none());
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
@ -3367,7 +3367,7 @@ unsafe impl TrustedEntityBorrow for FilteredEntityRef<'_> {}
|
||||
/// .data::<&mut A>()
|
||||
/// .build();
|
||||
///
|
||||
/// let mut filtered_entity: FilteredEntityMut = query.single_mut(&mut world);
|
||||
/// let mut filtered_entity: FilteredEntityMut = query.single_mut(&mut world).unwrap();
|
||||
/// let component: Mut<A> = filtered_entity.get_mut().unwrap();
|
||||
///
|
||||
/// // Here `FilteredEntityMut` is nested in a tuple, so it does not have access to `&mut A`.
|
||||
@ -3375,7 +3375,7 @@ unsafe impl TrustedEntityBorrow for FilteredEntityRef<'_> {}
|
||||
/// .data::<&mut A>()
|
||||
/// .build();
|
||||
///
|
||||
/// let (_, mut filtered_entity) = query.single_mut(&mut world);
|
||||
/// let (_, mut filtered_entity) = query.single_mut(&mut world).unwrap();
|
||||
/// assert!(filtered_entity.get_mut::<A>().is_none());
|
||||
/// ```
|
||||
pub struct FilteredEntityMut<'w> {
|
||||
|
@ -226,7 +226,7 @@ pub fn dispatch_focused_input<E: Event + Clone>(
|
||||
windows: Query<Entity, With<PrimaryWindow>>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
if let Ok(window) = windows.get_single() {
|
||||
if let Ok(window) = windows.single() {
|
||||
// If an element has keyboard focus, then dispatch the input event to that element.
|
||||
if let Some(focused_entity) = focus.0 {
|
||||
for ev in key_events.read() {
|
||||
|
@ -251,7 +251,7 @@ impl<'a> RenderViewIrradianceVolumeBindGroupEntries<'a> {
|
||||
fallback_image,
|
||||
)
|
||||
} else {
|
||||
RenderViewIrradianceVolumeBindGroupEntries::get_single(
|
||||
RenderViewIrradianceVolumeBindGroupEntries::single(
|
||||
render_view_irradiance_volumes,
|
||||
images,
|
||||
fallback_image,
|
||||
@ -295,7 +295,7 @@ impl<'a> RenderViewIrradianceVolumeBindGroupEntries<'a> {
|
||||
/// Looks up and returns the bindings for any irradiance volumes visible in
|
||||
/// the view, as well as the sampler. This is the version used when binding
|
||||
/// arrays aren't available on the current platform.
|
||||
fn get_single(
|
||||
fn single(
|
||||
render_view_irradiance_volumes: Option<&RenderViewLightProbes<IrradianceVolume>>,
|
||||
images: &'a RenderAssets<GpuImage>,
|
||||
fallback_image: &'a FallbackImage,
|
||||
|
@ -118,7 +118,7 @@ pub fn mouse_pick_events(
|
||||
WindowEvent::CursorMoved(event) => {
|
||||
let location = Location {
|
||||
target: match RenderTarget::Window(WindowRef::Entity(event.window))
|
||||
.normalize(primary_window.get_single().ok())
|
||||
.normalize(primary_window.single().ok())
|
||||
{
|
||||
Some(target) => target,
|
||||
None => continue,
|
||||
@ -138,7 +138,7 @@ pub fn mouse_pick_events(
|
||||
WindowEvent::MouseButtonInput(input) => {
|
||||
let location = Location {
|
||||
target: match RenderTarget::Window(WindowRef::Entity(input.window))
|
||||
.normalize(primary_window.get_single().ok())
|
||||
.normalize(primary_window.single().ok())
|
||||
{
|
||||
Some(target) => target,
|
||||
None => continue,
|
||||
@ -162,7 +162,7 @@ pub fn mouse_pick_events(
|
||||
|
||||
let location = Location {
|
||||
target: match RenderTarget::Window(WindowRef::Entity(window))
|
||||
.normalize(primary_window.get_single().ok())
|
||||
.normalize(primary_window.single().ok())
|
||||
{
|
||||
Some(target) => target,
|
||||
None => continue,
|
||||
@ -195,7 +195,7 @@ pub fn touch_pick_events(
|
||||
let pointer = PointerId::Touch(touch.id);
|
||||
let location = Location {
|
||||
target: match RenderTarget::Window(WindowRef::Entity(touch.window))
|
||||
.normalize(primary_window.get_single().ok())
|
||||
.normalize(primary_window.single().ok())
|
||||
{
|
||||
Some(target) => target,
|
||||
None => continue,
|
||||
|
@ -224,7 +224,7 @@ impl Location {
|
||||
) -> bool {
|
||||
if camera
|
||||
.target
|
||||
.normalize(Some(match primary_window.get_single() {
|
||||
.normalize(Some(match primary_window.single() {
|
||||
Ok(w) => w,
|
||||
Err(_) => return false,
|
||||
}))
|
||||
|
@ -317,7 +317,7 @@ impl Plugin for RenderPlugin {
|
||||
let primary_window = app
|
||||
.world_mut()
|
||||
.query_filtered::<&RawHandleWrapperHolder, With<PrimaryWindow>>()
|
||||
.get_single(app.world())
|
||||
.single(app.world())
|
||||
.ok()
|
||||
.cloned();
|
||||
let settings = render_creation.clone();
|
||||
|
@ -539,7 +539,7 @@ mod tests {
|
||||
// Only one synchronized entity
|
||||
assert!(q.iter(&render_world).count() == 1);
|
||||
|
||||
let render_entity = q.get_single(&render_world).unwrap();
|
||||
let render_entity = q.single(&render_world).unwrap();
|
||||
let render_entity_component = main_world.get::<RenderEntity>(main_entity).unwrap();
|
||||
|
||||
assert!(render_entity_component.id() == render_entity);
|
||||
|
@ -588,7 +588,8 @@ mod tests {
|
||||
let (scene_entity, scene_component_a) = app
|
||||
.world_mut()
|
||||
.query::<(Entity, &ComponentA)>()
|
||||
.single(app.world());
|
||||
.single(app.world())
|
||||
.unwrap();
|
||||
assert_eq!(scene_component_a.x, 3.0);
|
||||
assert_eq!(scene_component_a.y, 4.0);
|
||||
assert_eq!(
|
||||
@ -631,7 +632,10 @@ mod tests {
|
||||
|
||||
// clone only existing entity
|
||||
let mut scene_spawner = SceneSpawner::default();
|
||||
let entity = world.query_filtered::<Entity, With<A>>().single(&world);
|
||||
let entity = world
|
||||
.query_filtered::<Entity, With<A>>()
|
||||
.single(&world)
|
||||
.unwrap();
|
||||
let scene = DynamicSceneBuilder::from_world(&world)
|
||||
.extract_entity(entity)
|
||||
.build();
|
||||
|
@ -763,12 +763,12 @@ mod tests {
|
||||
|
||||
let bar_to_foo = dst_world
|
||||
.query_filtered::<&MyEntityRef, Without<Foo>>()
|
||||
.get_single(&dst_world)
|
||||
.single(&dst_world)
|
||||
.cloned()
|
||||
.unwrap();
|
||||
let foo = dst_world
|
||||
.query_filtered::<Entity, With<Foo>>()
|
||||
.get_single(&dst_world)
|
||||
.single(&dst_world)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(foo, bar_to_foo.0);
|
||||
@ -793,7 +793,7 @@ mod tests {
|
||||
deserialized_scene
|
||||
.write_to_world(&mut world, &mut EntityHashMap::default())
|
||||
.unwrap();
|
||||
assert_eq!(&qux, world.query::<&Qux>().single(&world));
|
||||
assert_eq!(&qux, world.query::<&Qux>().single(&world).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -115,7 +115,7 @@ fn sprite_picking(
|
||||
-transform.translation().z
|
||||
});
|
||||
|
||||
let primary_window = primary_window.get_single().ok();
|
||||
let primary_window = primary_window.single().ok();
|
||||
|
||||
for (pointer, location) in pointers.iter().filter_map(|(pointer, pointer_location)| {
|
||||
pointer_location.location().map(|loc| (pointer, loc))
|
||||
|
@ -153,7 +153,7 @@ pub fn extract_text2d_sprite(
|
||||
) {
|
||||
// TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621
|
||||
let scale_factor = windows
|
||||
.get_single()
|
||||
.single()
|
||||
.map(|window| window.resolution.scale_factor())
|
||||
.unwrap_or(1.0);
|
||||
let scaling = GlobalTransform::from_scale(Vec2::splat(scale_factor.recip()).extend(1.));
|
||||
@ -256,7 +256,7 @@ pub fn update_text2d_layout(
|
||||
) {
|
||||
// TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621
|
||||
let scale_factor = windows
|
||||
.get_single()
|
||||
.single()
|
||||
.ok()
|
||||
.map(|window| window.resolution.scale_factor())
|
||||
.or(*last_scale_factor)
|
||||
|
@ -43,7 +43,7 @@ fn calc_bounds(
|
||||
Ref<GlobalTransform>,
|
||||
)>,
|
||||
) {
|
||||
if let Ok((camera, camera_transform)) = camera.get_single() {
|
||||
if let Ok((camera, camera_transform)) = camera.single() {
|
||||
for (mut accessible, node, transform) in &mut nodes {
|
||||
if node.is_changed() || transform.is_changed() {
|
||||
if let Ok(translation) =
|
||||
|
@ -726,7 +726,7 @@ mod tests {
|
||||
mut cameras: Query<&mut Camera>,
|
||||
) {
|
||||
let primary_window = primary_window_query
|
||||
.get_single()
|
||||
.single()
|
||||
.expect("missing primary window");
|
||||
let camera_count = cameras.iter().len();
|
||||
for (camera_index, mut camera) in cameras.iter_mut().enumerate() {
|
||||
@ -783,7 +783,7 @@ mod tests {
|
||||
ui_schedule.run(world);
|
||||
let (ui_node_entity, UiTargetCamera(target_camera_entity)) = world
|
||||
.query_filtered::<(Entity, &UiTargetCamera), With<MovingUiNode>>()
|
||||
.get_single(world)
|
||||
.single(world)
|
||||
.expect("missing MovingUiNode");
|
||||
assert_eq!(expected_camera_entity, target_camera_entity);
|
||||
let mut ui_surface = world.resource_mut::<UiSurface>();
|
||||
|
@ -84,7 +84,7 @@ pub fn ui_picking(
|
||||
.map(|(entity, camera, _)| {
|
||||
(
|
||||
entity,
|
||||
camera.target.normalize(primary_window.get_single().ok()),
|
||||
camera.target.normalize(primary_window.single().ok()),
|
||||
)
|
||||
})
|
||||
.filter_map(|(entity, target)| Some(entity).zip(target))
|
||||
|
@ -2691,7 +2691,7 @@ pub struct DefaultUiCamera<'w, 's> {
|
||||
|
||||
impl<'w, 's> DefaultUiCamera<'w, 's> {
|
||||
pub fn get(&self) -> Option<Entity> {
|
||||
self.default_cameras.get_single().ok().or_else(|| {
|
||||
self.default_cameras.single().ok().or_else(|| {
|
||||
// If there isn't a single camera and the query isn't empty, there is two or more cameras queried.
|
||||
if !self.default_cameras.is_empty() {
|
||||
once!(warn!("Two or more Entities with IsDefaultUiCamera found when only one Camera with this marker is allowed."));
|
||||
|
@ -183,7 +183,7 @@ fn update_accessibility_nodes(
|
||||
)>,
|
||||
node_entities: Query<Entity, With<AccessibilityNode>>,
|
||||
) {
|
||||
let Ok((primary_window_id, primary_window)) = primary_window.get_single() else {
|
||||
let Ok((primary_window_id, primary_window)) = primary_window.single() else {
|
||||
return;
|
||||
};
|
||||
let Some(adapter) = adapters.get_mut(&primary_window_id) else {
|
||||
|
@ -551,7 +551,7 @@ impl<T: Event> WinitAppRunnerState<T> {
|
||||
let mut query = self
|
||||
.world_mut()
|
||||
.query_filtered::<Entity, With<PrimaryWindow>>();
|
||||
let entity = query.single(&self.world());
|
||||
let entity = query.single(&self.world()).unwrap();
|
||||
self.world_mut()
|
||||
.entity_mut(entity)
|
||||
.remove::<RawHandleWrapper>();
|
||||
@ -571,7 +571,7 @@ impl<T: Event> WinitAppRunnerState<T> {
|
||||
// handle wrapper removed when the app was suspended.
|
||||
let mut query = self.world_mut()
|
||||
.query_filtered::<(Entity, &Window), (With<CachedWindow>, Without<bevy_window::RawHandleWrapper>)>();
|
||||
if let Ok((entity, window)) = query.get_single(&self.world()) {
|
||||
if let Ok((entity, window)) = query.single(&self.world()) {
|
||||
let window = window.clone();
|
||||
|
||||
let mut create_window =
|
||||
|
@ -15,7 +15,7 @@ fn draw_cursor(
|
||||
window: Query<&Window>,
|
||||
mut gizmos: Gizmos,
|
||||
) {
|
||||
let Ok(window) = window.get_single() else {
|
||||
let Ok(window) = window.single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
@ -16,7 +16,7 @@ fn draw_cursor(
|
||||
windows: Query<&Window>,
|
||||
mut gizmos: Gizmos,
|
||||
) {
|
||||
let Ok(windows) = windows.get_single() else {
|
||||
let Ok(windows) = windows.single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
@ -78,7 +78,7 @@ fn input_handler(
|
||||
time: Res<Time>,
|
||||
) {
|
||||
if keyboard_input.just_pressed(KeyCode::Space) {
|
||||
let mesh_handle = mesh_query.get_single().expect("Query not successful");
|
||||
let mesh_handle = mesh_query.single().expect("Query not successful");
|
||||
let mesh = meshes.get_mut(mesh_handle).unwrap();
|
||||
toggle_texture(mesh);
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ fn drag_drop_image(
|
||||
mat.base_color_texture = Some(new_image.clone());
|
||||
|
||||
// Despawn the image viewer instructions
|
||||
if let Ok(text_entity) = text.get_single() {
|
||||
if let Ok(text_entity) = text.single() {
|
||||
commands.entity(text_entity).despawn();
|
||||
}
|
||||
}
|
||||
|
@ -258,7 +258,7 @@ fn get_async_loading_state(
|
||||
// If loaded, change the state.
|
||||
if is_loaded {
|
||||
next_loading_state.set(LoadingState::Loaded);
|
||||
if let Ok(mut text) = text.get_single_mut() {
|
||||
if let Ok(mut text) = text.single_mut() {
|
||||
"Loaded!".clone_into(&mut **text);
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
struct MyMusic;
|
||||
|
||||
fn update_speed(music_controller: Query<&AudioSink, With<MyMusic>>, time: Res<Time>) {
|
||||
let Ok(sink) = music_controller.get_single() else {
|
||||
let Ok(sink) = music_controller.single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@ -46,7 +46,7 @@ fn pause(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
music_controller: Query<&AudioSink, With<MyMusic>>,
|
||||
) {
|
||||
let Ok(sink) = music_controller.get_single() else {
|
||||
let Ok(sink) = music_controller.single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@ -59,7 +59,7 @@ fn mute(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mut music_controller: Query<&mut AudioSink, With<MyMusic>>,
|
||||
) {
|
||||
let Ok(mut sink) = music_controller.get_single_mut() else {
|
||||
let Ok(mut sink) = music_controller.single_mut() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@ -72,7 +72,7 @@ fn volume(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mut music_controller: Query<&mut AudioSink, With<MyMusic>>,
|
||||
) {
|
||||
let Ok(mut sink) = music_controller.get_single_mut() else {
|
||||
let Ok(mut sink) = music_controller.single_mut() else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
@ -165,7 +165,7 @@ fn screen_shake(
|
||||
}
|
||||
} else {
|
||||
// return camera to the latest position of player (it's fixed in this example case)
|
||||
if let Ok((mut camera, mut transform)) = query.get_single_mut() {
|
||||
if let Ok((mut camera, mut transform)) = query.single_mut() {
|
||||
let sub_view = camera.sub_camera_view.as_mut().unwrap();
|
||||
let target = screen_shake.latest_position.unwrap();
|
||||
sub_view
|
||||
|
@ -69,7 +69,7 @@ fn list_all_named_entities(
|
||||
text_string.push_str(&format!("{:?}\n", name));
|
||||
}
|
||||
|
||||
if let Ok(mut text) = name_text_query.get_single_mut() {
|
||||
if let Ok(mut text) = name_text_query.single_mut() {
|
||||
*text = Text::new(text_string);
|
||||
} else {
|
||||
commands.spawn((
|
||||
|
@ -175,7 +175,7 @@ fn handle_click(
|
||||
windows: Query<&Window>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
let Ok(windows) = windows.get_single() else {
|
||||
let Ok(windows) = windows.single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
@ -43,7 +43,7 @@ fn move_system(mut sprites: Query<(&mut Transform, &Velocity)>) {
|
||||
|
||||
// Bounce sprites outside the window
|
||||
fn bounce_system(window: Query<&Window>, mut sprites: Query<(&Transform, &mut Velocity)>) {
|
||||
let Ok(window) = window.get_single() else {
|
||||
let Ok(window) = window.single() else {
|
||||
return;
|
||||
};
|
||||
let width = window.width();
|
||||
|
@ -256,7 +256,7 @@ fn collisions(
|
||||
mut query: Query<(&mut Velocity, &mut Transform), With<Contributor>>,
|
||||
mut rng: ResMut<SharedRng>,
|
||||
) {
|
||||
let Ok(window) = window.get_single() else {
|
||||
let Ok(window) = window.single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
@ -112,7 +112,7 @@ fn run_camera_controller(
|
||||
) {
|
||||
let dt = time.delta_secs();
|
||||
|
||||
let Ok((mut transform, mut controller)) = query.get_single_mut() else {
|
||||
let Ok((mut transform, mut controller)) = query.single_mut() else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
@ -61,7 +61,7 @@ fn touch_camera(
|
||||
mut last_position: Local<Option<Vec2>>,
|
||||
mut rotations: EventReader<RotationGesture>,
|
||||
) {
|
||||
let Ok(window) = window.get_single() else {
|
||||
let Ok(window) = window.single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
@ -333,7 +333,7 @@ fn mouse_handler(
|
||||
mut rng: Local<Option<ChaCha8Rng>>,
|
||||
mut wave: Local<usize>,
|
||||
) {
|
||||
let Ok(window) = window.get_single() else {
|
||||
let Ok(window) = window.single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@ -534,7 +534,7 @@ fn handle_collision(half_extents: Vec2, translation: &Vec3, velocity: &mut Vec3)
|
||||
}
|
||||
}
|
||||
fn collision_system(window: Query<&Window>, mut bird_query: Query<(&mut Bird, &Transform)>) {
|
||||
let Ok(window) = window.get_single() else {
|
||||
let Ok(window) = window.single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
@ -26,7 +26,7 @@ fn setup(
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
window: Query<&Window>,
|
||||
) {
|
||||
) -> Result {
|
||||
// circular base
|
||||
commands.spawn((
|
||||
Mesh3d(meshes.add(Circle::new(4.0))),
|
||||
@ -56,7 +56,7 @@ fn setup(
|
||||
}
|
||||
|
||||
// cameras
|
||||
let window = window.single();
|
||||
let window = window.single()?;
|
||||
let width = window.resolution.width() / CAMERA_COLS as f32 * window.resolution.scale_factor();
|
||||
let height = window.resolution.height() / CAMERA_ROWS as f32 * window.resolution.scale_factor();
|
||||
let mut i = 0;
|
||||
@ -83,6 +83,7 @@ fn setup(
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn rotate_cameras(time: Res<Time>, mut query: Query<&mut Transform, With<Camera>>) {
|
||||
|
@ -155,7 +155,9 @@ fn setup(mut commands: Commands, font: Res<FontHandle>, args: Res<Args>) {
|
||||
|
||||
// System for rotating and translating the camera
|
||||
fn move_camera(time: Res<Time>, mut camera_query: Query<&mut Transform, With<Camera>>) {
|
||||
let mut camera_transform = camera_query.single_mut();
|
||||
let Ok(mut camera_transform) = camera_query.single_mut() else {
|
||||
return;
|
||||
};
|
||||
camera_transform.rotate_z(time.delta_secs() * 0.5);
|
||||
*camera_transform =
|
||||
*camera_transform * Transform::from_translation(Vec3::X * CAMERA_SPEED * time.delta_secs());
|
||||
|
@ -188,7 +188,7 @@ fn update_text(
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(text) = texts.get_single() else {
|
||||
let Ok(text) = texts.single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
@ -92,7 +92,7 @@ fn handle_input(
|
||||
mut dir: ResMut<ResizeDir>,
|
||||
example_text: Query<Entity, With<Text>>,
|
||||
mut writer: TextUiWriter,
|
||||
) {
|
||||
) -> Result {
|
||||
use LeftClickAction::*;
|
||||
if input.just_pressed(KeyCode::KeyA) {
|
||||
*action = match *action {
|
||||
@ -100,7 +100,7 @@ fn handle_input(
|
||||
Resize => Nothing,
|
||||
Nothing => Move,
|
||||
};
|
||||
*writer.text(example_text.single(), 4) = format!("{:?}", *action);
|
||||
*writer.text(example_text.single()?, 4) = format!("{:?}", *action);
|
||||
}
|
||||
|
||||
if input.just_pressed(KeyCode::KeyS) {
|
||||
@ -108,13 +108,15 @@ fn handle_input(
|
||||
.0
|
||||
.checked_sub(1)
|
||||
.unwrap_or(DIRECTIONS.len().saturating_sub(1));
|
||||
*writer.text(example_text.single(), 7) = format!("{:?}", DIRECTIONS[dir.0]);
|
||||
*writer.text(example_text.single()?, 7) = format!("{:?}", DIRECTIONS[dir.0]);
|
||||
}
|
||||
|
||||
if input.just_pressed(KeyCode::KeyD) {
|
||||
dir.0 = (dir.0 + 1) % DIRECTIONS.len();
|
||||
*writer.text(example_text.single(), 7) = format!("{:?}", DIRECTIONS[dir.0]);
|
||||
*writer.text(example_text.single()?, 7) = format!("{:?}", DIRECTIONS[dir.0]);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn move_or_resize_windows(
|
||||
|
@ -39,7 +39,7 @@ fn spawn_player(mut commands: Commands) {
|
||||
|
||||
fn spell_casting(mut player: Query<&mut Player>, keyboard_input: Res<ButtonInput<KeyCode>>) {
|
||||
if keyboard_input.just_pressed(KeyCode::Space) {
|
||||
let Ok(mut player) = player.get_single_mut() else {
|
||||
let Ok(mut player) = player.single_mut() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@ -76,7 +76,7 @@ fn test_player_spawn() {
|
||||
// Now that the startup systems have run, we can check if the player has
|
||||
// spawned as expected.
|
||||
let expected = Player::default();
|
||||
let actual = app.world_mut().query::<&Player>().get_single(app.world());
|
||||
let actual = app.world_mut().query::<&Player>().single(app.world());
|
||||
assert!(actual.is_ok(), "There should be exactly 1 player.");
|
||||
assert_eq!(
|
||||
expected.mana,
|
||||
@ -98,7 +98,11 @@ fn test_spell_casting() {
|
||||
app.update();
|
||||
|
||||
let expected = Player::default();
|
||||
let actual = app.world_mut().query::<&Player>().single(app.world());
|
||||
let actual = app
|
||||
.world_mut()
|
||||
.query::<&Player>()
|
||||
.single(app.world())
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
expected.mana - 1,
|
||||
actual.mana,
|
||||
@ -112,7 +116,11 @@ fn test_spell_casting() {
|
||||
app.update();
|
||||
|
||||
// No extra spells have been cast, so no mana should have been used.
|
||||
let after_keypress_event = app.world_mut().query::<&Player>().single(app.world());
|
||||
let after_keypress_event = app
|
||||
.world_mut()
|
||||
.query::<&Player>()
|
||||
.single(app.world())
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
expected.mana - 1,
|
||||
after_keypress_event.mana,
|
||||
@ -127,6 +135,10 @@ fn test_window_title() {
|
||||
|
||||
app.update();
|
||||
|
||||
let window = app.world_mut().query::<&Window>().single(app.world());
|
||||
let window = app
|
||||
.world_mut()
|
||||
.query::<&Window>()
|
||||
.single(app.world())
|
||||
.unwrap();
|
||||
assert_eq!(window.title, "This is window 0!");
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ fn startup(mut cmds: Commands) {
|
||||
}
|
||||
|
||||
fn toggle_window_mode(mut qry_window: Query<&mut Window>) {
|
||||
let Ok(mut window) = qry_window.get_single_mut() else {
|
||||
let Ok(mut window) = qry_window.single_mut() else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user