From da4e7769ad41182cfbc25ac8c659c1e5773611bd Mon Sep 17 00:00:00 2001 From: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Date: Thu, 10 Oct 2024 20:12:01 -0700 Subject: [PATCH] bevy_ecs: Special-case `Entity::PLACEHOLDER` formatting (#15839) # Objective Oftentimes, users will store an entity on a component or resource. To make this component/resource `Default`-able, they might initialize it with `Entity::PLACEHOLDER`. This is sometimes done to avoid the need for an `Option`, especially if it complicates other logic. For example, it's used in this `Selection` resource to denote "no selection": ```rust #[derive(Resource, Debug)] struct Selection(Entity); impl Default for Selection { fn default() -> Self { Self(Entity::PLACEHOLDER) } } ``` The problem is that if we try to `Debug` the current `Selection`, we get back: `4294967295v1#8589934591`. It's not immediately obvious whether or not the entity is an actual entity or the placeholder. Now while it doesn't take long to realize that this is in fact just the value of `Entity::PLACEHOLDER`, it would be a lot clearer if this was made explicit, especially for these particular use cases. ## Solution This PR makes the `Debug` and `Display` impls for `Entity` return `PLACEHOLDER` for the `Entity::PLACEHOLDER` constant. ~~Feel free to bikeshed the actual value returned here. I think `PLACEHOLDER` on its own could work too.~~ Swapped to `PLACEHOLDER` from `Entity::PLACEHOLDER`. ## Testing You can test locally by running: ``` cargo test --package bevy_ecs ``` --- ## Migration Guide The `Debug` and `Display` impls for `Entity` now return `PLACEHOLDER` for the `Entity::PLACEHOLDER` constant. If you had any code relying on these values, you may need to account for this change. --- crates/bevy_ecs/src/entity/mod.rs | 43 +++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/crates/bevy_ecs/src/entity/mod.rs b/crates/bevy_ecs/src/entity/mod.rs index 75df8e1bb61fb..eaee9b28d3ed8 100644 --- a/crates/bevy_ecs/src/entity/mod.rs +++ b/crates/bevy_ecs/src/entity/mod.rs @@ -397,6 +397,8 @@ impl<'de> Deserialize<'de> for Entity { /// /// This takes the format: `{index}v{generation}#{bits}`. /// +/// For [`Entity::PLACEHOLDER`], this outputs `PLACEHOLDER`. +/// /// # Usage /// /// Prefer to use this format for debugging and logging purposes. Because the output contains @@ -416,22 +418,32 @@ impl<'de> Deserialize<'de> for Entity { /// ``` impl fmt::Debug for Entity { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}v{}#{}", - self.index(), - self.generation(), - self.to_bits() - ) + if self == &Self::PLACEHOLDER { + write!(f, "PLACEHOLDER") + } else { + write!( + f, + "{}v{}#{}", + self.index(), + self.generation(), + self.to_bits() + ) + } } } /// Outputs the short entity identifier, including the index and generation. /// /// This takes the format: `{index}v{generation}`. +/// +/// For [`Entity::PLACEHOLDER`], this outputs `PLACEHOLDER`. impl fmt::Display for Entity { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}v{}", self.index(), self.generation()) + if self == &Self::PLACEHOLDER { + write!(f, "PLACEHOLDER") + } else { + write!(f, "{}v{}", self.index(), self.generation()) + } } } @@ -1195,16 +1207,21 @@ mod tests { fn entity_debug() { let entity = Entity::from_raw(42); let string = format!("{:?}", entity); - assert!(string.contains("42")); - assert!(string.contains("v1")); - assert!(string.contains(format!("#{}", entity.to_bits()).as_str())); + assert_eq!(string, "42v1#4294967338"); + + let entity = Entity::PLACEHOLDER; + let string = format!("{:?}", entity); + assert_eq!(string, "PLACEHOLDER"); } #[test] fn entity_display() { let entity = Entity::from_raw(42); let string = format!("{}", entity); - assert!(string.contains("42")); - assert!(string.contains("v1")); + assert_eq!(string, "42v1"); + + let entity = Entity::PLACEHOLDER; + let string = format!("{}", entity); + assert_eq!(string, "PLACEHOLDER"); } }