Path: blob/main/crates/bevy_reflect/src/struct_trait.rs
6598 views
use crate::generics::impl_generic_info_methods;1use crate::{2attributes::{impl_custom_attribute_methods, CustomAttributes},3type_info::impl_type_methods,4ApplyError, Generics, NamedField, PartialReflect, Reflect, ReflectKind, ReflectMut,5ReflectOwned, ReflectRef, Type, TypeInfo, TypePath,6};7use alloc::{borrow::Cow, boxed::Box, vec::Vec};8use bevy_platform::collections::HashMap;9use bevy_platform::sync::Arc;10use bevy_reflect_derive::impl_type_path;11use core::{12fmt::{Debug, Formatter},13slice::Iter,14};1516/// A trait used to power [struct-like] operations via [reflection].17///18/// This trait uses the [`Reflect`] trait to allow implementors to have their fields19/// be dynamically addressed by both name and index.20///21/// When using [`#[derive(Reflect)]`](derive@crate::Reflect) on a standard struct,22/// this trait will be automatically implemented.23/// This goes for [unit structs] as well.24///25/// # Example26///27/// ```28/// use bevy_reflect::{PartialReflect, Reflect, Struct};29///30/// #[derive(Reflect)]31/// struct Foo {32/// bar: u32,33/// }34///35/// let foo = Foo { bar: 123 };36///37/// assert_eq!(foo.field_len(), 1);38/// assert_eq!(foo.name_at(0), Some("bar"));39///40/// let field: &dyn PartialReflect = foo.field("bar").unwrap();41/// assert_eq!(field.try_downcast_ref::<u32>(), Some(&123));42/// ```43///44/// [struct-like]: https://doc.rust-lang.org/book/ch05-01-defining-structs.html45/// [reflection]: crate46/// [unit structs]: https://doc.rust-lang.org/book/ch05-01-defining-structs.html#unit-like-structs-without-any-fields47pub trait Struct: PartialReflect {48/// Returns a reference to the value of the field named `name` as a `&dyn49/// PartialReflect`.50fn field(&self, name: &str) -> Option<&dyn PartialReflect>;5152/// Returns a mutable reference to the value of the field named `name` as a53/// `&mut dyn PartialReflect`.54fn field_mut(&mut self, name: &str) -> Option<&mut dyn PartialReflect>;5556/// Returns a reference to the value of the field with index `index` as a57/// `&dyn PartialReflect`.58fn field_at(&self, index: usize) -> Option<&dyn PartialReflect>;5960/// Returns a mutable reference to the value of the field with index `index`61/// as a `&mut dyn PartialReflect`.62fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn PartialReflect>;6364/// Returns the name of the field with index `index`.65fn name_at(&self, index: usize) -> Option<&str>;6667/// Returns the number of fields in the struct.68fn field_len(&self) -> usize;6970/// Returns an iterator over the values of the reflectable fields for this struct.71fn iter_fields(&self) -> FieldIter<'_>;7273/// Creates a new [`DynamicStruct`] from this struct.74fn to_dynamic_struct(&self) -> DynamicStruct {75let mut dynamic_struct = DynamicStruct::default();76dynamic_struct.set_represented_type(self.get_represented_type_info());77for (i, value) in self.iter_fields().enumerate() {78dynamic_struct.insert_boxed(self.name_at(i).unwrap(), value.to_dynamic());79}80dynamic_struct81}8283/// Will return `None` if [`TypeInfo`] is not available.84fn get_represented_struct_info(&self) -> Option<&'static StructInfo> {85self.get_represented_type_info()?.as_struct().ok()86}87}8889/// A container for compile-time named struct info.90#[derive(Clone, Debug)]91pub struct StructInfo {92ty: Type,93generics: Generics,94fields: Box<[NamedField]>,95field_names: Box<[&'static str]>,96field_indices: HashMap<&'static str, usize>,97custom_attributes: Arc<CustomAttributes>,98#[cfg(feature = "documentation")]99docs: Option<&'static str>,100}101102impl StructInfo {103/// Create a new [`StructInfo`].104///105/// # Arguments106///107/// * `fields`: The fields of this struct in the order they are defined108pub fn new<T: Reflect + TypePath>(fields: &[NamedField]) -> Self {109let field_indices = fields110.iter()111.enumerate()112.map(|(index, field)| (field.name(), index))113.collect::<HashMap<_, _>>();114115let field_names = fields.iter().map(NamedField::name).collect();116117Self {118ty: Type::of::<T>(),119generics: Generics::new(),120fields: fields.to_vec().into_boxed_slice(),121field_names,122field_indices,123custom_attributes: Arc::new(CustomAttributes::default()),124#[cfg(feature = "documentation")]125docs: None,126}127}128129/// Sets the docstring for this struct.130#[cfg(feature = "documentation")]131pub fn with_docs(self, docs: Option<&'static str>) -> Self {132Self { docs, ..self }133}134135/// Sets the custom attributes for this struct.136pub fn with_custom_attributes(self, custom_attributes: CustomAttributes) -> Self {137Self {138custom_attributes: Arc::new(custom_attributes),139..self140}141}142143/// A slice containing the names of all fields in order.144pub fn field_names(&self) -> &[&'static str] {145&self.field_names146}147148/// Get the field with the given name.149pub fn field(&self, name: &str) -> Option<&NamedField> {150self.field_indices151.get(name)152.map(|index| &self.fields[*index])153}154155/// Get the field at the given index.156pub fn field_at(&self, index: usize) -> Option<&NamedField> {157self.fields.get(index)158}159160/// Get the index of the field with the given name.161pub fn index_of(&self, name: &str) -> Option<usize> {162self.field_indices.get(name).copied()163}164165/// Iterate over the fields of this struct.166pub fn iter(&self) -> Iter<'_, NamedField> {167self.fields.iter()168}169170/// The total number of fields in this struct.171pub fn field_len(&self) -> usize {172self.fields.len()173}174175impl_type_methods!(ty);176177/// The docstring of this struct, if any.178#[cfg(feature = "documentation")]179pub fn docs(&self) -> Option<&'static str> {180self.docs181}182183impl_custom_attribute_methods!(self.custom_attributes, "struct");184185impl_generic_info_methods!(generics);186}187188/// An iterator over the field values of a struct.189pub struct FieldIter<'a> {190pub(crate) struct_val: &'a dyn Struct,191pub(crate) index: usize,192}193194impl<'a> FieldIter<'a> {195/// Creates a new [`FieldIter`].196pub fn new(value: &'a dyn Struct) -> Self {197FieldIter {198struct_val: value,199index: 0,200}201}202}203204impl<'a> Iterator for FieldIter<'a> {205type Item = &'a dyn PartialReflect;206207fn next(&mut self) -> Option<Self::Item> {208let value = self.struct_val.field_at(self.index);209self.index += value.is_some() as usize;210value211}212213fn size_hint(&self) -> (usize, Option<usize>) {214let size = self.struct_val.field_len();215(size, Some(size))216}217}218219impl<'a> ExactSizeIterator for FieldIter<'a> {}220221/// A convenience trait which combines fetching and downcasting of struct222/// fields.223///224/// # Example225///226/// ```227/// use bevy_reflect::{GetField, Reflect};228///229/// #[derive(Reflect)]230/// struct Foo {231/// bar: String,232/// }233///234/// # fn main() {235/// let mut foo = Foo { bar: "Hello, world!".to_string() };236///237/// foo.get_field_mut::<String>("bar").unwrap().truncate(5);238/// assert_eq!(foo.get_field::<String>("bar"), Some(&"Hello".to_string()));239/// # }240/// ```241pub trait GetField {242/// Returns a reference to the value of the field named `name`, downcast to243/// `T`.244fn get_field<T: Reflect>(&self, name: &str) -> Option<&T>;245246/// Returns a mutable reference to the value of the field named `name`,247/// downcast to `T`.248fn get_field_mut<T: Reflect>(&mut self, name: &str) -> Option<&mut T>;249}250251impl<S: Struct> GetField for S {252fn get_field<T: Reflect>(&self, name: &str) -> Option<&T> {253self.field(name)254.and_then(|value| value.try_downcast_ref::<T>())255}256257fn get_field_mut<T: Reflect>(&mut self, name: &str) -> Option<&mut T> {258self.field_mut(name)259.and_then(|value| value.try_downcast_mut::<T>())260}261}262263impl GetField for dyn Struct {264fn get_field<T: Reflect>(&self, name: &str) -> Option<&T> {265self.field(name)266.and_then(|value| value.try_downcast_ref::<T>())267}268269fn get_field_mut<T: Reflect>(&mut self, name: &str) -> Option<&mut T> {270self.field_mut(name)271.and_then(|value| value.try_downcast_mut::<T>())272}273}274275/// A struct type which allows fields to be added at runtime.276#[derive(Default)]277pub struct DynamicStruct {278represented_type: Option<&'static TypeInfo>,279fields: Vec<Box<dyn PartialReflect>>,280field_names: Vec<Cow<'static, str>>,281field_indices: HashMap<Cow<'static, str>, usize>,282}283284impl DynamicStruct {285/// Sets the [type] to be represented by this `DynamicStruct`.286///287/// # Panics288///289/// Panics if the given [type] is not a [`TypeInfo::Struct`].290///291/// [type]: TypeInfo292pub fn set_represented_type(&mut self, represented_type: Option<&'static TypeInfo>) {293if let Some(represented_type) = represented_type {294assert!(295matches!(represented_type, TypeInfo::Struct(_)),296"expected TypeInfo::Struct but received: {represented_type:?}"297);298}299300self.represented_type = represented_type;301}302303/// Inserts a field named `name` with value `value` into the struct.304///305/// If the field already exists, it is overwritten.306pub fn insert_boxed<'a>(307&mut self,308name: impl Into<Cow<'a, str>>,309value: Box<dyn PartialReflect>,310) {311let name: Cow<str> = name.into();312if let Some(index) = self.field_indices.get(&name) {313self.fields[*index] = value;314} else {315self.fields.push(value);316self.field_indices317.insert(Cow::Owned(name.clone().into_owned()), self.fields.len() - 1);318self.field_names.push(Cow::Owned(name.into_owned()));319}320}321322/// Inserts a field named `name` with the typed value `value` into the struct.323///324/// If the field already exists, it is overwritten.325pub fn insert<'a, T: PartialReflect>(&mut self, name: impl Into<Cow<'a, str>>, value: T) {326self.insert_boxed(name, Box::new(value));327}328329/// Gets the index of the field with the given name.330pub fn index_of(&self, name: &str) -> Option<usize> {331self.field_indices.get(name).copied()332}333}334335impl Struct for DynamicStruct {336#[inline]337fn field(&self, name: &str) -> Option<&dyn PartialReflect> {338self.field_indices339.get(name)340.map(|index| &*self.fields[*index])341}342343#[inline]344fn field_mut(&mut self, name: &str) -> Option<&mut dyn PartialReflect> {345if let Some(index) = self.field_indices.get(name) {346Some(&mut *self.fields[*index])347} else {348None349}350}351352#[inline]353fn field_at(&self, index: usize) -> Option<&dyn PartialReflect> {354self.fields.get(index).map(|value| &**value)355}356357#[inline]358fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn PartialReflect> {359self.fields.get_mut(index).map(|value| &mut **value)360}361362#[inline]363fn name_at(&self, index: usize) -> Option<&str> {364self.field_names.get(index).map(AsRef::as_ref)365}366367#[inline]368fn field_len(&self) -> usize {369self.fields.len()370}371372#[inline]373fn iter_fields(&self) -> FieldIter<'_> {374FieldIter {375struct_val: self,376index: 0,377}378}379}380381impl PartialReflect for DynamicStruct {382#[inline]383fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {384self.represented_type385}386387#[inline]388fn into_partial_reflect(self: Box<Self>) -> Box<dyn PartialReflect> {389self390}391392#[inline]393fn as_partial_reflect(&self) -> &dyn PartialReflect {394self395}396397#[inline]398fn as_partial_reflect_mut(&mut self) -> &mut dyn PartialReflect {399self400}401402fn try_into_reflect(self: Box<Self>) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>> {403Err(self)404}405fn try_as_reflect(&self) -> Option<&dyn Reflect> {406None407}408fn try_as_reflect_mut(&mut self) -> Option<&mut dyn Reflect> {409None410}411412fn try_apply(&mut self, value: &dyn PartialReflect) -> Result<(), ApplyError> {413let struct_value = value.reflect_ref().as_struct()?;414415for (i, value) in struct_value.iter_fields().enumerate() {416let name = struct_value.name_at(i).unwrap();417if let Some(v) = self.field_mut(name) {418v.try_apply(value)?;419}420}421422Ok(())423}424425#[inline]426fn reflect_kind(&self) -> ReflectKind {427ReflectKind::Struct428}429430#[inline]431fn reflect_ref(&self) -> ReflectRef<'_> {432ReflectRef::Struct(self)433}434435#[inline]436fn reflect_mut(&mut self) -> ReflectMut<'_> {437ReflectMut::Struct(self)438}439440#[inline]441fn reflect_owned(self: Box<Self>) -> ReflectOwned {442ReflectOwned::Struct(self)443}444445fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option<bool> {446struct_partial_eq(self, value)447}448449fn debug(&self, f: &mut Formatter<'_>) -> core::fmt::Result {450write!(f, "DynamicStruct(")?;451struct_debug(self, f)?;452write!(f, ")")453}454455#[inline]456fn is_dynamic(&self) -> bool {457true458}459}460461impl_type_path!((in bevy_reflect) DynamicStruct);462463impl Debug for DynamicStruct {464fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {465self.debug(f)466}467}468469impl<'a, N> FromIterator<(N, Box<dyn PartialReflect>)> for DynamicStruct470where471N: Into<Cow<'a, str>>,472{473/// Create a dynamic struct that doesn't represent a type from the474/// field name, field value pairs.475fn from_iter<I: IntoIterator<Item = (N, Box<dyn PartialReflect>)>>(fields: I) -> Self {476let mut dynamic_struct = Self::default();477for (name, value) in fields.into_iter() {478dynamic_struct.insert_boxed(name, value);479}480dynamic_struct481}482}483484impl IntoIterator for DynamicStruct {485type Item = Box<dyn PartialReflect>;486type IntoIter = alloc::vec::IntoIter<Self::Item>;487488fn into_iter(self) -> Self::IntoIter {489self.fields.into_iter()490}491}492493impl<'a> IntoIterator for &'a DynamicStruct {494type Item = &'a dyn PartialReflect;495type IntoIter = FieldIter<'a>;496497fn into_iter(self) -> Self::IntoIter {498self.iter_fields()499}500}501502/// Compares a [`Struct`] with a [`PartialReflect`] value.503///504/// Returns true if and only if all of the following are true:505/// - `b` is a struct;506/// - For each field in `a`, `b` contains a field with the same name and507/// [`PartialReflect::reflect_partial_eq`] returns `Some(true)` for the two field508/// values.509///510/// Returns [`None`] if the comparison couldn't even be performed.511#[inline]512pub fn struct_partial_eq<S: Struct + ?Sized>(a: &S, b: &dyn PartialReflect) -> Option<bool> {513let ReflectRef::Struct(struct_value) = b.reflect_ref() else {514return Some(false);515};516517if a.field_len() != struct_value.field_len() {518return Some(false);519}520521for (i, value) in struct_value.iter_fields().enumerate() {522let name = struct_value.name_at(i).unwrap();523if let Some(field_value) = a.field(name) {524let eq_result = field_value.reflect_partial_eq(value);525if let failed @ (Some(false) | None) = eq_result {526return failed;527}528} else {529return Some(false);530}531}532533Some(true)534}535536/// The default debug formatter for [`Struct`] types.537///538/// # Example539/// ```540/// use bevy_reflect::Reflect;541/// #[derive(Reflect)]542/// struct MyStruct {543/// foo: usize544/// }545///546/// let my_struct: &dyn Reflect = &MyStruct { foo: 123 };547/// println!("{:#?}", my_struct);548///549/// // Output:550///551/// // MyStruct {552/// // foo: 123,553/// // }554/// ```555#[inline]556pub fn struct_debug(dyn_struct: &dyn Struct, f: &mut Formatter<'_>) -> core::fmt::Result {557let mut debug = f.debug_struct(558dyn_struct559.get_represented_type_info()560.map(TypeInfo::type_path)561.unwrap_or("_"),562);563for field_index in 0..dyn_struct.field_len() {564let field = dyn_struct.field_at(field_index).unwrap();565debug.field(566dyn_struct.name_at(field_index).unwrap(),567&field as &dyn Debug,568);569}570debug.finish()571}572573#[cfg(test)]574mod tests {575use crate::*;576#[derive(Reflect, Default)]577struct MyStruct {578a: (),579b: (),580c: (),581}582#[test]583fn next_index_increment() {584let my_struct = MyStruct::default();585let mut iter = my_struct.iter_fields();586iter.index = iter.len() - 1;587let prev_index = iter.index;588assert!(iter.next().is_some());589assert_eq!(prev_index, iter.index - 1);590591// When None we should no longer increase index592let prev_index = iter.index;593assert!(iter.next().is_none());594assert_eq!(prev_index, iter.index);595assert!(iter.next().is_none());596assert_eq!(prev_index, iter.index);597}598}599600601