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<'w, 's> core::fmt::Display for NameOrEntityItem<'w, 's> {
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}
162
163impl From<String> for Name {
164    #[inline(always)]
165    fn from(name: String) -> Self {
166        Name::new(name)
167    }
168}
169
170// Conversions to strings
171
172impl AsRef<str> for Name {
173    #[inline(always)]
174    fn as_ref(&self) -> &str {
175        &self.name
176    }
177}
178
179impl From<&Name> for String {
180    #[inline(always)]
181    fn from(val: &Name) -> String {
182        val.as_str().to_owned()
183    }
184}
185
186impl From<Name> for String {
187    #[inline(always)]
188    fn from(val: Name) -> String {
189        val.name.into_owned()
190    }
191}
192
193impl Hash for Name {
194    fn hash<H: Hasher>(&self, state: &mut H) {
195        self.name.hash(state);
196    }
197}
198
199impl PartialEq for Name {
200    fn eq(&self, other: &Self) -> bool {
201        if self.hash != other.hash {
202            // Makes the common case of two strings not been equal very fast
203            return false;
204        }
205
206        self.name.eq(&other.name)
207    }
208}
209
210impl Eq for Name {}
211
212impl PartialOrd for Name {
213    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
214        Some(self.cmp(other))
215    }
216}
217
218impl Ord for Name {
219    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
220        self.name.cmp(&other.name)
221    }
222}
223
224impl Deref for Name {
225    type Target = str;
226
227    fn deref(&self) -> &Self::Target {
228        self.name.as_ref()
229    }
230}
231
232#[cfg(feature = "serialize")]
233impl Serialize for Name {
234    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
235        serializer.serialize_str(self.as_str())
236    }
237}
238
239#[cfg(feature = "serialize")]
240impl<'de> Deserialize<'de> for Name {
241    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
242        deserializer.deserialize_str(NameVisitor)
243    }
244}
245
246#[cfg(feature = "serialize")]
247struct NameVisitor;
248
249#[cfg(feature = "serialize")]
250impl<'de> Visitor<'de> for NameVisitor {
251    type Value = Name;
252
253    fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
254        formatter.write_str(core::any::type_name::<Name>())
255    }
256
257    fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
258        Ok(Name::new(v.to_string()))
259    }
260
261    fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
262        Ok(Name::new(v))
263    }
264}
265
266#[cfg(test)]
267mod tests {
268    use super::*;
269    use crate::world::World;
270    use alloc::string::ToString;
271
272    #[test]
273    fn test_display_of_debug_name() {
274        let mut world = World::new();
275        let e1 = world.spawn_empty().id();
276        let name = Name::new("MyName");
277        let e2 = world.spawn(name.clone()).id();
278        let mut query = world.query::<NameOrEntity>();
279        let d1 = query.get(&world, e1).unwrap();
280        // NameOrEntity Display for entities without a Name should be {index}v{generation}
281        assert_eq!(d1.to_string(), "0v0");
282        let d2 = query.get(&world, e2).unwrap();
283        // NameOrEntity Display for entities with a Name should be the Name
284        assert_eq!(d2.to_string(), "MyName");
285    }
286}
287
288#[cfg(all(test, feature = "serialize"))]
289mod serde_tests {
290    use super::Name;
291
292    use serde_test::{assert_tokens, Token};
293
294    #[test]
295    fn test_serde_name() {
296        let name = Name::new("MyComponent");
297        assert_tokens(&name, &[Token::String("MyComponent")]);
298    }
299}