Skip to main content

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::Hashed;
10use core::{
11    hash::{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(pub HashedStr);
51
52impl Default for Name {
53    fn default() -> Self {
54        Name::new("")
55    }
56}
57
58/// A wrapper over Hashed. This exists to make Name("value".into()) possible, which plays nicely with contexts like the `bsn!` macro.
59#[derive(Clone)]
60#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
61pub struct HashedStr(Hashed<Cow<'static, str>>);
62
63impl From<&'static str> for HashedStr {
64    fn from(value: &'static str) -> Self {
65        Self(Hashed::new(Cow::Borrowed(value)))
66    }
67}
68
69impl From<String> for HashedStr {
70    fn from(value: String) -> Self {
71        Self(Hashed::new(Cow::Owned(value)))
72    }
73}
74
75impl Name {
76    /// Creates a new [`Name`] from any string-like type.
77    ///
78    /// The internal hash will be computed immediately.
79    pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
80        Self(HashedStr(Hashed::new(name.into())))
81    }
82
83    /// Sets the entity's name.
84    ///
85    /// The internal hash will be re-computed.
86    #[inline(always)]
87    pub fn set(&mut self, name: impl Into<Cow<'static, str>>) {
88        *self = Name::new(name);
89    }
90
91    /// Updates the name of the entity in place.
92    ///
93    /// This will allocate a new string if the name was previously
94    /// created from a borrow.
95    #[inline(always)]
96    pub fn mutate(&mut self, func: impl FnOnce(&mut String)) {
97        self.0 .0.mutate(|cow_str| match cow_str {
98            Cow::Borrowed(borrowed) => {
99                let mut owned = borrowed.to_owned();
100                func(&mut owned);
101                *cow_str = Cow::Owned(owned);
102            }
103            Cow::Owned(owned) => {
104                func(owned);
105            }
106        });
107    }
108
109    /// Gets the name of the entity as a `&str`.
110    #[inline(always)]
111    pub fn as_str(&self) -> &str {
112        &self.0 .0
113    }
114    /// Get the precomputed hash of this names string, useful for raw entry operations on [`PreHashMap`](bevy_utils::PreHashMap)
115    #[inline(always)]
116    pub fn pre_hash(&self) -> u64 {
117        self.0 .0.hash()
118    }
119}
120
121impl core::fmt::Display for Name {
122    #[inline(always)]
123    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
124        core::fmt::Display::fmt(&*self.0 .0, f)
125    }
126}
127
128impl core::fmt::Debug for Name {
129    #[inline(always)]
130    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
131        core::fmt::Debug::fmt(&self.0 .0, f)
132    }
133}
134
135/// Convenient query for giving a human friendly name to an entity.
136///
137/// ```
138/// # use bevy_ecs::prelude::*;
139/// # #[derive(Component)] pub struct Score(f32);
140/// fn increment_score(mut scores: Query<(NameOrEntity, &mut Score)>) {
141///     for (name, mut score) in &mut scores {
142///         score.0 += 1.0;
143///         if score.0.is_nan() {
144///             log::error!("Score for {name} is invalid");
145///         }
146///     }
147/// }
148/// # bevy_ecs::system::assert_is_system(increment_score);
149/// ```
150///
151/// # Implementation
152///
153/// The `Display` impl for `NameOrEntity` returns the `Name` where there is one
154/// or {index}v{generation} for entities without one.
155#[derive(QueryData)]
156#[query_data(derive(Debug))]
157pub struct NameOrEntity {
158    /// A [`Name`] that the entity might have that is displayed if available.
159    pub name: Option<&'static Name>,
160    /// The unique identifier of the entity as a fallback.
161    pub entity: Entity,
162}
163
164impl<'w, 's> core::fmt::Display for NameOrEntityItem<'w, 's> {
165    #[inline(always)]
166    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
167        match self.name {
168            Some(name) => core::fmt::Display::fmt(name, f),
169            None => core::fmt::Display::fmt(&self.entity, f),
170        }
171    }
172}
173
174// Conversions from strings
175
176impl From<&str> for Name {
177    #[inline(always)]
178    fn from(name: &str) -> Self {
179        Name::new(name.to_owned())
180    }
181}
182
183impl From<String> for Name {
184    #[inline(always)]
185    fn from(name: String) -> Self {
186        Name::new(name)
187    }
188}
189
190// Conversions to strings
191
192impl AsRef<str> for Name {
193    #[inline(always)]
194    fn as_ref(&self) -> &str {
195        &self.0 .0
196    }
197}
198
199impl From<&Name> for String {
200    #[inline(always)]
201    fn from(val: &Name) -> String {
202        val.as_str().to_owned()
203    }
204}
205
206impl From<Name> for String {
207    #[inline(always)]
208    fn from(val: Name) -> String {
209        val.as_str().to_owned()
210    }
211}
212
213impl Hash for Name {
214    fn hash<H: Hasher>(&self, state: &mut H) {
215        Hash::hash(&self.0 .0, state);
216    }
217}
218
219impl PartialEq for Name {
220    fn eq(&self, other: &Self) -> bool {
221        if self.0 .0.hash() != other.0 .0.hash() {
222            // Makes the common case of two strings not been equal very fast
223            return false;
224        }
225
226        self.0 .0.eq(&other.0 .0)
227    }
228}
229
230impl Eq for Name {}
231
232impl PartialOrd for Name {
233    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
234        Some(self.cmp(other))
235    }
236}
237
238impl Ord for Name {
239    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
240        self.0 .0.cmp(&other.0 .0)
241    }
242}
243
244impl Deref for Name {
245    type Target = str;
246
247    fn deref(&self) -> &Self::Target {
248        self.as_str()
249    }
250}
251
252#[cfg(feature = "serialize")]
253impl Serialize for Name {
254    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
255        serializer.serialize_str(self.as_str())
256    }
257}
258
259#[cfg(feature = "serialize")]
260impl<'de> Deserialize<'de> for Name {
261    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
262        deserializer.deserialize_str(NameVisitor)
263    }
264}
265
266#[cfg(feature = "serialize")]
267struct NameVisitor;
268
269#[cfg(feature = "serialize")]
270impl<'de> Visitor<'de> for NameVisitor {
271    type Value = Name;
272
273    fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
274        formatter.write_str(core::any::type_name::<Name>())
275    }
276
277    fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
278        Ok(Name::new(v.to_string()))
279    }
280
281    fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
282        Ok(Name::new(v))
283    }
284}
285
286#[cfg(test)]
287mod tests {
288    use super::*;
289    use crate::world::World;
290    use alloc::string::ToString;
291    use bevy_platform::hash::fixed_hash_one;
292
293    #[test]
294    fn test_display_of_debug_name() {
295        let mut world = World::new();
296        let e1 = world.spawn_empty().id();
297        let name = Name::new("MyName");
298        let e2 = world.spawn(name.clone()).id();
299        let mut query = world.query::<NameOrEntity>();
300        let d1 = query.get(&world, e1).unwrap();
301        // NameOrEntity Display for entities without a Name should be {index}v{generation}
302        assert_eq!(d1.to_string(), "1v0");
303        let d2 = query.get(&world, e2).unwrap();
304        // NameOrEntity Display for entities with a Name should be the Name
305        assert_eq!(d2.to_string(), "MyName");
306    }
307    #[test]
308    fn test_name_hash_is_fixed() {
309        let str = "foobar";
310        assert_eq!(Name::from(str).pre_hash(), fixed_hash_one(str));
311    }
312}
313
314#[cfg(all(test, feature = "serialize"))]
315mod serde_tests {
316    use super::Name;
317
318    use serde_test::{assert_tokens, Token};
319
320    #[test]
321    fn test_serde_name() {
322        let name = Name::new("MyComponent");
323        assert_tokens(&name, &[Token::String("MyComponent")]);
324    }
325}