Skip to content

Commit 17b1bcd

Browse files
authored
Add missing insert API commands (#15166)
# Objective - Adds the missing API commands `insert_if_new_and` and `try_insert_if_new_and` (resolves #15105) - Adds some test coverage for existing insert commands ## Testing - Implemented additional unit tests to add coverage
1 parent 382917f commit 17b1bcd

File tree

1 file changed

+110
-2
lines changed
  • crates/bevy_ecs/src/system/commands

1 file changed

+110
-2
lines changed

crates/bevy_ecs/src/system/commands/mod.rs

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,13 +1017,36 @@ impl EntityCommands<'_> {
10171017
///
10181018
/// # Panics
10191019
///
1020+
/// The command will panic when applied if the associated entity does not exist.
1021+
///
1022+
/// To avoid a panic in this case, use the command [`Self::try_insert_if_new`] instead.
1023+
pub fn insert_if_new(self, bundle: impl Bundle) -> Self {
1024+
self.add(insert(bundle, InsertMode::Keep))
1025+
}
1026+
1027+
/// Adds a [`Bundle`] of components to the entity without overwriting if the
1028+
/// predicate returns true.
1029+
///
1030+
/// This is the same as [`EntityCommands::insert_if`], but in case of duplicate
1031+
/// components will leave the old values instead of replacing them with new
1032+
/// ones.
1033+
///
1034+
/// # Panics
1035+
///
10201036
/// The command will panic when applied if the associated entity does not
10211037
/// exist.
10221038
///
10231039
/// To avoid a panic in this case, use the command [`Self::try_insert_if_new`]
10241040
/// instead.
1025-
pub fn insert_if_new(self, bundle: impl Bundle) -> Self {
1026-
self.add(insert(bundle, InsertMode::Keep))
1041+
pub fn insert_if_new_and<F>(self, bundle: impl Bundle, condition: F) -> Self
1042+
where
1043+
F: FnOnce() -> bool,
1044+
{
1045+
if condition() {
1046+
self.insert_if_new(bundle)
1047+
} else {
1048+
self
1049+
}
10271050
}
10281051

10291052
/// Adds a dynamic component to an entity.
@@ -1161,6 +1184,52 @@ impl EntityCommands<'_> {
11611184
}
11621185
}
11631186

1187+
/// Tries to add a [`Bundle`] of components to the entity without overwriting if the
1188+
/// predicate returns true.
1189+
///
1190+
/// This is the same as [`EntityCommands::try_insert_if`], but in case of duplicate
1191+
/// components will leave the old values instead of replacing them with new
1192+
/// ones.
1193+
///
1194+
/// # Note
1195+
///
1196+
/// Unlike [`Self::insert_if_new_and`], this will not panic if the associated entity does
1197+
/// not exist.
1198+
///
1199+
/// # Example
1200+
///
1201+
/// ```
1202+
/// # use bevy_ecs::prelude::*;
1203+
/// # #[derive(Resource)]
1204+
/// # struct PlayerEntity { entity: Entity }
1205+
/// # impl PlayerEntity { fn is_spectator(&self) -> bool { true } }
1206+
/// #[derive(Component)]
1207+
/// struct StillLoadingStats;
1208+
/// #[derive(Component)]
1209+
/// struct Health(u32);
1210+
///
1211+
/// fn add_health_system(mut commands: Commands, player: Res<PlayerEntity>) {
1212+
/// commands.entity(player.entity)
1213+
/// .try_insert_if(Health(10), || player.is_spectator())
1214+
/// .remove::<StillLoadingStats>();
1215+
///
1216+
/// commands.entity(player.entity)
1217+
/// // This will not panic nor will it overwrite the component
1218+
/// .try_insert_if_new_and(Health(5), || player.is_spectator());
1219+
/// }
1220+
/// # bevy_ecs::system::assert_is_system(add_health_system);
1221+
/// ```
1222+
pub fn try_insert_if_new_and<F>(self, bundle: impl Bundle, condition: F) -> Self
1223+
where
1224+
F: FnOnce() -> bool,
1225+
{
1226+
if condition() {
1227+
self.try_insert_if_new(bundle)
1228+
} else {
1229+
self
1230+
}
1231+
}
1232+
11641233
/// Tries to add a [`Bundle`] of components to the entity without overwriting.
11651234
///
11661235
/// This is the same as [`EntityCommands::try_insert`], but in case of duplicate
@@ -1684,6 +1753,45 @@ mod tests {
16841753
assert_eq!(results3, vec![(42u32, 0u64), (0u32, 42u64)]);
16851754
}
16861755

1756+
#[test]
1757+
fn insert_components() {
1758+
let mut world = World::default();
1759+
let mut command_queue1 = CommandQueue::default();
1760+
1761+
// insert components
1762+
let entity = Commands::new(&mut command_queue1, &world)
1763+
.spawn(())
1764+
.insert_if(W(1u8), || true)
1765+
.insert_if(W(2u8), || false)
1766+
.insert_if_new(W(1u16))
1767+
.insert_if_new(W(2u16))
1768+
.insert_if_new_and(W(1u32), || false)
1769+
.insert_if_new_and(W(2u32), || true)
1770+
.insert_if_new_and(W(3u32), || true)
1771+
.id();
1772+
command_queue1.apply(&mut world);
1773+
1774+
let results = world
1775+
.query::<(&W<u8>, &W<u16>, &W<u32>)>()
1776+
.iter(&world)
1777+
.map(|(a, b, c)| (a.0, b.0, c.0))
1778+
.collect::<Vec<_>>();
1779+
assert_eq!(results, vec![(1u8, 1u16, 2u32)]);
1780+
1781+
// try to insert components after despawning entity
1782+
// in another command queue
1783+
Commands::new(&mut command_queue1, &world)
1784+
.entity(entity)
1785+
.try_insert_if_new_and(W(1u64), || true);
1786+
1787+
let mut command_queue2 = CommandQueue::default();
1788+
Commands::new(&mut command_queue2, &world)
1789+
.entity(entity)
1790+
.despawn();
1791+
command_queue2.apply(&mut world);
1792+
command_queue1.apply(&mut world);
1793+
}
1794+
16871795
#[test]
16881796
fn remove_components() {
16891797
let mut world = World::default();

0 commit comments

Comments
 (0)