bevy_ecs/
name.rs

1//! Provides the [`Name`] [`Component`], used for identifying an [`Entity`].
2
3use crate::{component::Component, entity::Entity, query::QueryData};
4
5use alloc::{
6    borrow::{Cow, ToOwned},
7    string::String,
8};
9use bevy_platform::hash::FixedHasher;
10use core::{
11    hash::{BuildHasher, Hash, Hasher},
12    ops::Deref,
13};
14
15#[cfg(feature = "serialize")]
16use {
17    alloc::string::ToString,
18    serde::{
19        de::{Error, Visitor},
20        Deserialize, Deserializer, Serialize, Serializer,
21    },
22};
23
24#[cfg(feature = "bevy_reflect")]
25use {
26    crate::reflect::ReflectComponent,
27    bevy_reflect::{std_traits::ReflectDefault, Reflect},
28};
29
30#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
31use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
32
33/// Component used to identify an entity. Stores a hash for faster comparisons.
34///
35/// The hash is eagerly re-computed upon each update to the name.
36///
37/// [`Name`] should not be treated as a globally unique identifier for entities,
38/// as multiple entities can have the same name.  [`Entity`] should be
39/// used instead as the default unique identifier.
40#[derive(Component, Clone)]
41#[cfg_attr(
42    feature = "bevy_reflect",
43    derive(Reflect),
44    reflect(Component, Default, Debug, Clone, Hash, PartialEq)
45)]
46#[cfg_attr(
47    all(feature = "serialize", feature = "bevy_reflect"),
48    reflect(Deserialize, Serialize)
49)]
50pub struct Name {
51    hash: u64, // Won't be serialized
52    name: Cow<'static, str>,
53}
54
55impl Default for Name {
56    fn default() -> Self {
57        Name::new("")
58    }
59}
60
61impl Name {
62    /// Creates a new [`Name`] from any string-like type.
63    ///
64    /// The internal hash will be computed immediately.
65    pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
66        let name = name.into();
67        let mut name = Name { name, hash: 0 };
68        name.update_hash();
69        name
70    }
71
72    /// Sets the entity's name.
73    ///
74    /// The internal hash will be re-computed.
75    #[inline(always)]
76    pub fn set(&mut self, name: impl Into<Cow<'static, str>>) {
77        *self = Name::new(name);
78    }
79
80    /// Updates the name of the entity in place.
81    ///
82    /// This will allocate a new string if the name was previously
83    /// created from a borrow.
84    #[inline(always)]
85    pub fn mutate<F: FnOnce(&mut String)>(&mut self, f: F) {
86        f(self.name.to_mut());
87        self.update_hash();
88    }
89
90    /// Gets the name of the entity as a `&str`.
91    #[inline(always)]
92    pub fn as_str(&self) -> &str {
93        &self.name
94    }
95
96    fn update_hash(&mut self) {
97        self.hash = FixedHasher.hash_one(&self.name);
98    }
99}
100
101impl core::fmt::Display for Name {
102    #[inline(always)]
103    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
104        core::fmt::Display::fmt(&self.name, f)
105    }
106}
107
108impl core::fmt::Debug for Name {
109    #[inline(always)]
110    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
111        core::fmt::Debug::fmt(&self.name, f)
112    }
113}
114
115/// Convenient query for giving a human friendly name to an entity.
116///
117/// ```
118/// # use bevy_ecs::prelude::*;
119/// # #[derive(Component)] pub struct Score(f32);
120/// fn increment_score(mut scores: Query<(NameOrEntity, &mut Score)>) {
121///     for (name, mut score) in &mut scores {
122///         score.0 += 1.0;
123///         if score.0.is_nan() {
124///             log::error!("Score for {name} is invalid");
125///         }
126///     }
127/// }
128/// # bevy_ecs::system::assert_is_system(increment_score);
129/// ```
130///
131/// # Implementation
132///
133/// The `Display` impl for `NameOrEntity` returns the `Name` where there is one
134/// or {index}v{generation} for entities without one.
135#[derive(QueryData)]
136#[query_data(derive(Debug))]
137pub struct NameOrEntity {
138    /// A [`Name`] that the entity might have that is displayed if available.
139    pub name: Option<&'static Name>,
140    /// The unique identifier of the entity as a fallback.
141    pub entity: Entity,
142}
143
144impl<'a> core::fmt::Display for NameOrEntityItem<'a> {
145    #[inline(always)]
146    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
147        match self.name {
148            Some(name) => core::fmt::Display::fmt(name, f),
149            None => core::fmt::Display::fmt(&self.entity, f),
150        }
151    }
152}
153
154// Conversions from strings
155
156impl From<&str> for Name {
157    #[inline(always)]
158    fn from(name: &str) -> Self {
159        Name::new(name.to_owned())
160    }
161}
162impl From<String> for Name {
163    #[inline(always)]
164    fn from(name: String) -> Self {
165        Name::new(name)
166    }
167}
168
169// Conversions to strings
170
171impl AsRef<str> for Name {
172    #[inline(always)]
173    fn as_ref(&self) -> &str {
174        &self.name
175    }
176}
177impl From<&Name> for String {
178    #[inline(always)]
179    fn from(val: &Name) -> String {
180        val.as_str().to_owned()
181    }
182}
183impl From<Name> for String {
184    #[inline(always)]
185    fn from(val: Name) -> String {
186        val.name.into_owned()
187    }
188}
189
190impl Hash for Name {
191    fn hash<H: Hasher>(&self, state: &mut H) {
192        self.name.hash(state);
193    }
194}
195
196impl PartialEq for Name {
197    fn eq(&self, other: &Self) -> bool {
198        if self.hash != other.hash {
199            // Makes the common case of two strings not been equal very fast
200            return false;
201        }
202
203        self.name.eq(&other.name)
204    }
205}
206
207impl Eq for Name {}
208
209impl PartialOrd for Name {
210    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
211        Some(self.cmp(other))
212    }
213}
214
215impl Ord for Name {
216    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
217        self.name.cmp(&other.name)
218    }
219}
220
221impl Deref for Name {
222    type Target = str;
223
224    fn deref(&self) -> &Self::Target {
225        self.name.as_ref()
226    }
227}
228
229#[cfg(feature = "serialize")]
230impl Serialize for Name {
231    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
232        serializer.serialize_str(self.as_str())
233    }
234}
235
236#[cfg(feature = "serialize")]
237impl<'de> Deserialize<'de> for Name {
238    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
239        deserializer.deserialize_str(NameVisitor)
240    }
241}
242
243#[cfg(feature = "serialize")]
244struct NameVisitor;
245
246#[cfg(feature = "serialize")]
247impl<'de> Visitor<'de> for NameVisitor {
248    type Value = Name;
249
250    fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
251        formatter.write_str(core::any::type_name::<Name>())
252    }
253
254    fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
255        Ok(Name::new(v.to_string()))
256    }
257
258    fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
259        Ok(Name::new(v))
260    }
261}
262
263#[cfg(test)]
264mod tests {
265    use super::*;
266    use crate::world::World;
267    use alloc::string::ToString;
268
269    #[test]
270    fn test_display_of_debug_name() {
271        let mut world = World::new();
272        let e1 = world.spawn_empty().id();
273        let name = Name::new("MyName");
274        let e2 = world.spawn(name.clone()).id();
275        let mut query = world.query::<NameOrEntity>();
276        let d1 = query.get(&world, e1).unwrap();
277        let d2 = query.get(&world, e2).unwrap();
278        // NameOrEntity Display for entities without a Name should be {index}v{generation}
279        assert_eq!(d1.to_string(), "0v1");
280        // NameOrEntity Display for entities with a Name should be the Name
281        assert_eq!(d2.to_string(), "MyName");
282    }
283}
284
285#[cfg(all(test, feature = "serialize"))]
286mod serde_tests {
287    use super::Name;
288
289    use serde_test::{assert_tokens, Token};
290
291    #[test]
292    fn test_serde_name() {
293        let name = Name::new("MyComponent");
294        assert_tokens(&name, &[Token::String("MyComponent")]);
295    }
296}