Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/name.rs
6598 views
1
//! Provides the [`Name`] [`Component`], used for identifying an [`Entity`].
2
3
use crate::{component::Component, entity::Entity, query::QueryData};
4
5
use alloc::{
6
borrow::{Cow, ToOwned},
7
string::String,
8
};
9
use bevy_platform::hash::FixedHasher;
10
use core::{
11
hash::{BuildHasher, Hash, Hasher},
12
ops::Deref,
13
};
14
15
#[cfg(feature = "serialize")]
16
use {
17
alloc::string::ToString,
18
serde::{
19
de::{Error, Visitor},
20
Deserialize, Deserializer, Serialize, Serializer,
21
},
22
};
23
24
#[cfg(feature = "bevy_reflect")]
25
use {
26
crate::reflect::ReflectComponent,
27
bevy_reflect::{std_traits::ReflectDefault, Reflect},
28
};
29
30
#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
31
use 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
)]
50
pub struct Name {
51
hash: u64, // Won't be serialized
52
name: Cow<'static, str>,
53
}
54
55
impl Default for Name {
56
fn default() -> Self {
57
Name::new("")
58
}
59
}
60
61
impl 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
101
impl 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
108
impl 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))]
137
pub 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
144
impl<'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
156
impl From<&str> for Name {
157
#[inline(always)]
158
fn from(name: &str) -> Self {
159
Name::new(name.to_owned())
160
}
161
}
162
163
impl 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
172
impl AsRef<str> for Name {
173
#[inline(always)]
174
fn as_ref(&self) -> &str {
175
&self.name
176
}
177
}
178
179
impl From<&Name> for String {
180
#[inline(always)]
181
fn from(val: &Name) -> String {
182
val.as_str().to_owned()
183
}
184
}
185
186
impl From<Name> for String {
187
#[inline(always)]
188
fn from(val: Name) -> String {
189
val.name.into_owned()
190
}
191
}
192
193
impl Hash for Name {
194
fn hash<H: Hasher>(&self, state: &mut H) {
195
self.name.hash(state);
196
}
197
}
198
199
impl 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
210
impl Eq for Name {}
211
212
impl PartialOrd for Name {
213
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
214
Some(self.cmp(other))
215
}
216
}
217
218
impl Ord for Name {
219
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
220
self.name.cmp(&other.name)
221
}
222
}
223
224
impl 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")]
233
impl 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")]
240
impl<'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")]
247
struct NameVisitor;
248
249
#[cfg(feature = "serialize")]
250
impl<'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)]
267
mod 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"))]
289
mod 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
}
300
301