use crate::{component::Component, entity::Entity, query::QueryData};
use alloc::{
borrow::{Cow, ToOwned},
string::String,
};
use bevy_platform::hash::Hashed;
use core::{
hash::{Hash, Hasher},
ops::Deref,
};
#[cfg(feature = "serialize")]
use {
alloc::string::ToString,
serde::{
de::{Error, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
},
};
#[cfg(feature = "bevy_reflect")]
use {
crate::reflect::ReflectComponent,
bevy_reflect::{std_traits::ReflectDefault, Reflect},
};
#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
#[derive(Component, Clone)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Component, Default, Debug, Clone, Hash, PartialEq)
)]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Deserialize, Serialize)
)]
pub struct Name(pub HashedStr);
impl Default for Name {
fn default() -> Self {
Name::new("")
}
}
#[derive(Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
pub struct HashedStr(Hashed<Cow<'static, str>>);
impl From<&'static str> for HashedStr {
fn from(value: &'static str) -> Self {
Self(Hashed::new(Cow::Borrowed(value)))
}
}
impl From<String> for HashedStr {
fn from(value: String) -> Self {
Self(Hashed::new(Cow::Owned(value)))
}
}
impl Name {
pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
Self(HashedStr(Hashed::new(name.into())))
}
#[inline(always)]
pub fn set(&mut self, name: impl Into<Cow<'static, str>>) {
*self = Name::new(name);
}
#[inline(always)]
pub fn mutate(&mut self, func: impl FnOnce(&mut String)) {
self.0 .0.mutate(|cow_str| match cow_str {
Cow::Borrowed(borrowed) => {
let mut owned = borrowed.to_owned();
func(&mut owned);
*cow_str = Cow::Owned(owned);
}
Cow::Owned(owned) => {
func(owned);
}
});
}
#[inline(always)]
pub fn as_str(&self) -> &str {
&self.0 .0
}
#[inline(always)]
pub fn pre_hash(&self) -> u64 {
self.0 .0.hash()
}
}
impl core::fmt::Display for Name {
#[inline(always)]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
core::fmt::Display::fmt(&*self.0 .0, f)
}
}
impl core::fmt::Debug for Name {
#[inline(always)]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
core::fmt::Debug::fmt(&self.0 .0, f)
}
}
#[derive(QueryData)]
#[query_data(derive(Debug))]
pub struct NameOrEntity {
pub name: Option<&'static Name>,
pub entity: Entity,
}
impl<'w, 's> core::fmt::Display for NameOrEntityItem<'w, 's> {
#[inline(always)]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self.name {
Some(name) => core::fmt::Display::fmt(name, f),
None => core::fmt::Display::fmt(&self.entity, f),
}
}
}
impl From<&str> for Name {
#[inline(always)]
fn from(name: &str) -> Self {
Name::new(name.to_owned())
}
}
impl From<String> for Name {
#[inline(always)]
fn from(name: String) -> Self {
Name::new(name)
}
}
impl AsRef<str> for Name {
#[inline(always)]
fn as_ref(&self) -> &str {
&self.0 .0
}
}
impl From<&Name> for String {
#[inline(always)]
fn from(val: &Name) -> String {
val.as_str().to_owned()
}
}
impl From<Name> for String {
#[inline(always)]
fn from(val: Name) -> String {
val.as_str().to_owned()
}
}
impl Hash for Name {
fn hash<H: Hasher>(&self, state: &mut H) {
Hash::hash(&self.0 .0, state);
}
}
impl PartialEq for Name {
fn eq(&self, other: &Self) -> bool {
if self.0 .0.hash() != other.0 .0.hash() {
return false;
}
self.0 .0.eq(&other.0 .0)
}
}
impl Eq for Name {}
impl PartialOrd for Name {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Name {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.0 .0.cmp(&other.0 .0)
}
}
impl Deref for Name {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
#[cfg(feature = "serialize")]
impl Serialize for Name {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.as_str())
}
}
#[cfg(feature = "serialize")]
impl<'de> Deserialize<'de> for Name {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_str(NameVisitor)
}
}
#[cfg(feature = "serialize")]
struct NameVisitor;
#[cfg(feature = "serialize")]
impl<'de> Visitor<'de> for NameVisitor {
type Value = Name;
fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
formatter.write_str(core::any::type_name::<Name>())
}
fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
Ok(Name::new(v.to_string()))
}
fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
Ok(Name::new(v))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::world::World;
use alloc::string::ToString;
use bevy_platform::hash::fixed_hash_one;
#[test]
fn test_display_of_debug_name() {
let mut world = World::new();
let e1 = world.spawn_empty().id();
let name = Name::new("MyName");
let e2 = world.spawn(name.clone()).id();
let mut query = world.query::<NameOrEntity>();
let d1 = query.get(&world, e1).unwrap();
assert_eq!(d1.to_string(), "1v0");
let d2 = query.get(&world, e2).unwrap();
assert_eq!(d2.to_string(), "MyName");
}
#[test]
fn test_name_hash_is_fixed() {
let str = "foobar";
assert_eq!(Name::from(str).pre_hash(), fixed_hash_one(str));
}
}
#[cfg(all(test, feature = "serialize"))]
mod serde_tests {
use super::Name;
use serde_test::{assert_tokens, Token};
#[test]
fn test_serde_name() {
let name = Name::new("MyComponent");
assert_tokens(&name, &[Token::String("MyComponent")]);
}
}