Skip to main content

bevy_reflect/
structs.rs

1//! Traits and types used to power [struct-like] operations via reflection.
2//!
3//! [struct-like]: https://doc.rust-lang.org/book/ch05-01-defining-structs.html
4use crate::generics::impl_generic_info_methods;
5use crate::{
6    attributes::{impl_custom_attribute_methods, CustomAttributes},
7    ty::impl_type_methods,
8    ApplyError, Generics, NamedField, PartialReflect, Reflect, ReflectCloneError, ReflectKind,
9    ReflectMut, ReflectOwned, ReflectRef, Type, TypeInfo, TypePath,
10};
11use alloc::{borrow::Cow, boxed::Box, vec::Vec};
12use bevy_platform::collections::HashMap;
13use bevy_reflect_derive::impl_type_path;
14use core::{
15    fmt::{Debug, Formatter},
16    slice::Iter,
17};
18
19/// A trait used to power [struct-like] operations via [reflection].
20///
21/// This trait uses the [`Reflect`] trait to allow implementors to have their fields
22/// be dynamically addressed by both name and index.
23///
24/// When using [`#[derive(Reflect)]`](derive@crate::Reflect) on a standard struct,
25/// this trait will be automatically implemented.
26/// This goes for [unit structs] as well.
27///
28/// # Example
29///
30/// ```
31/// use bevy_reflect::{PartialReflect, Reflect, structs::Struct};
32///
33/// #[derive(Reflect)]
34/// struct Foo {
35///     bar: u32,
36/// }
37///
38/// let foo = Foo { bar: 123 };
39///
40/// assert_eq!(foo.field_len(), 1);
41/// assert_eq!(foo.name_at(0), Some("bar"));
42///
43/// let field: &dyn PartialReflect = foo.field("bar").unwrap();
44/// assert_eq!(field.try_downcast_ref::<u32>(), Some(&123));
45/// ```
46///
47/// [struct-like]: https://doc.rust-lang.org/book/ch05-01-defining-structs.html
48/// [reflection]: crate
49/// [unit structs]: https://doc.rust-lang.org/book/ch05-01-defining-structs.html#unit-like-structs-without-any-fields
50pub trait Struct: PartialReflect {
51    /// Gets a reference to the value of the field named `name` as a `&dyn
52    /// PartialReflect`.
53    fn field(&self, name: &str) -> Option<&dyn PartialReflect>;
54
55    /// Gets a mutable reference to the value of the field named `name` as a
56    /// `&mut dyn PartialReflect`.
57    fn field_mut(&mut self, name: &str) -> Option<&mut dyn PartialReflect>;
58
59    /// Gets a reference to the value of the field with index `index` as a
60    /// `&dyn PartialReflect`.
61    fn field_at(&self, index: usize) -> Option<&dyn PartialReflect>;
62
63    /// Gets a mutable reference to the value of the field with index `index`
64    /// as a `&mut dyn PartialReflect`.
65    fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn PartialReflect>;
66
67    /// Gets the name of the field with index `index`.
68    fn name_at(&self, index: usize) -> Option<&str>;
69
70    /// Gets the index of the field with the given name.
71    fn index_of_name(&self, name: &str) -> Option<usize>;
72
73    /// Returns the number of fields in the struct.
74    fn field_len(&self) -> usize;
75
76    /// Returns an iterator over the values of the reflectable fields for this struct.
77    fn iter_fields(&self) -> FieldIter<'_>;
78
79    /// Creates a new [`DynamicStruct`] from this struct.
80    ///
81    /// Returns an error if any field cannot be converted via [`PartialReflect::to_dynamic`].
82    fn to_dynamic_struct(&self) -> Result<DynamicStruct, ReflectCloneError> {
83        let mut dynamic_struct = DynamicStruct::default();
84        dynamic_struct.set_represented_type(self.get_represented_type_info());
85        for (name, value) in self.iter_fields() {
86            dynamic_struct.insert_boxed(name, value.to_dynamic()?);
87        }
88        Ok(dynamic_struct)
89    }
90
91    /// Will return `None` if [`TypeInfo`] is not available.
92    fn get_represented_struct_info(&self) -> Option<&'static StructInfo> {
93        self.get_represented_type_info()?.as_struct().ok()
94    }
95}
96
97impl<'a> IntoIterator for &'a dyn Struct {
98    type Item = (&'a str, &'a dyn PartialReflect);
99    type IntoIter = FieldIter<'a>;
100
101    fn into_iter(self) -> Self::IntoIter {
102        self.iter_fields()
103    }
104}
105
106/// A container for compile-time named struct info.
107#[derive(Clone, Debug)]
108pub struct StructInfo {
109    ty: Type,
110    generics: Generics,
111    fields: Box<[NamedField]>,
112    field_names: Box<[&'static str]>,
113    field_indices: HashMap<&'static str, usize>,
114    custom_attributes: CustomAttributes,
115    #[cfg(feature = "reflect_documentation")]
116    docs: Option<&'static str>,
117}
118
119impl StructInfo {
120    /// Create a new [`StructInfo`].
121    ///
122    /// # Arguments
123    ///
124    /// * `fields`: The fields of this struct in the order they are defined
125    pub fn new<T: Reflect + TypePath>(fields: &[NamedField]) -> Self {
126        Self::from_erased(fields, Type::of::<T>())
127    }
128
129    // Inlining is disabled because this function is called many times by cold
130    // functions inside generated code.
131    #[inline(never)]
132    fn from_erased(fields: &[NamedField], ty: Type) -> Self {
133        let field_indices = fields
134            .iter()
135            .enumerate()
136            .map(|(index, field)| (field.name(), index))
137            .collect::<HashMap<_, _>>();
138
139        let field_names = fields.iter().map(NamedField::name).collect();
140
141        Self {
142            ty,
143            generics: Generics::new(),
144            fields: fields.to_vec().into_boxed_slice(),
145            field_names,
146            field_indices,
147            custom_attributes: CustomAttributes::default(),
148            #[cfg(feature = "reflect_documentation")]
149            docs: None,
150        }
151    }
152
153    /// Sets the docstring for this struct.
154    #[cfg(feature = "reflect_documentation")]
155    pub fn with_docs(self, docs: Option<&'static str>) -> Self {
156        Self { docs, ..self }
157    }
158
159    /// Sets the custom attributes for this struct.
160    pub fn with_custom_attributes(self, custom_attributes: CustomAttributes) -> Self {
161        Self {
162            custom_attributes,
163            ..self
164        }
165    }
166
167    /// A slice containing the names of all fields in order.
168    pub fn field_names(&self) -> &[&'static str] {
169        &self.field_names
170    }
171
172    /// Get the field with the given name.
173    pub fn field(&self, name: &str) -> Option<&NamedField> {
174        self.field_indices
175            .get(name)
176            .map(|index| &self.fields[*index])
177    }
178
179    /// Get the field at the given index.
180    pub fn field_at(&self, index: usize) -> Option<&NamedField> {
181        self.fields.get(index)
182    }
183
184    /// Get the index of the field with the given name.
185    pub fn index_of(&self, name: &str) -> Option<usize> {
186        self.field_indices.get(name).copied()
187    }
188
189    /// Iterate over the fields of this struct.
190    pub fn iter(&self) -> Iter<'_, NamedField> {
191        self.fields.iter()
192    }
193
194    /// The total number of fields in this struct.
195    pub fn field_len(&self) -> usize {
196        self.fields.len()
197    }
198
199    impl_type_methods!(ty);
200
201    /// The docstring of this struct, if any.
202    #[cfg(feature = "reflect_documentation")]
203    pub fn docs(&self) -> Option<&'static str> {
204        self.docs
205    }
206
207    impl_custom_attribute_methods!(self.custom_attributes, "struct");
208
209    impl_generic_info_methods!(generics);
210}
211
212/// An iterator over the names and fields of a struct.
213pub struct FieldIter<'a> {
214    pub(crate) struct_val: &'a dyn Struct,
215    pub(crate) index: usize,
216}
217
218impl<'a> FieldIter<'a> {
219    /// Creates a new [`FieldIter`].
220    pub fn new(value: &'a dyn Struct) -> Self {
221        FieldIter {
222            struct_val: value,
223            index: 0,
224        }
225    }
226}
227
228impl<'a> Iterator for FieldIter<'a> {
229    type Item = (&'a str, &'a dyn PartialReflect);
230
231    fn next(&mut self) -> Option<Self::Item> {
232        if let Some(name) = self.struct_val.name_at(self.index)
233            && let Some(field) = self.struct_val.field_at(self.index)
234        {
235            self.index += 1;
236            Some((name, field))
237        } else {
238            None
239        }
240    }
241
242    fn size_hint(&self) -> (usize, Option<usize>) {
243        let remaining = self.struct_val.field_len().saturating_sub(self.index);
244        (remaining, Some(remaining))
245    }
246}
247
248impl<'a> ExactSizeIterator for FieldIter<'a> {}
249
250/// A convenience trait which combines fetching and downcasting of struct
251/// fields.
252///
253/// # Example
254///
255/// ```
256/// use bevy_reflect::{structs::GetField, Reflect};
257///
258/// #[derive(Reflect)]
259/// struct Foo {
260///     bar: String,
261/// }
262///
263/// # fn main() {
264/// let mut foo = Foo { bar: "Hello, world!".to_string() };
265///
266/// foo.get_field_mut::<String>("bar").unwrap().truncate(5);
267/// assert_eq!(foo.get_field::<String>("bar"), Some(&"Hello".to_string()));
268/// # }
269/// ```
270pub trait GetField {
271    /// Gets a reference to the value of the field named `name`, downcast to
272    /// `T`.
273    fn get_field<T: Reflect>(&self, name: &str) -> Option<&T>;
274
275    /// Gets a mutable reference to the value of the field named `name`,
276    /// downcast to `T`.
277    fn get_field_mut<T: Reflect>(&mut self, name: &str) -> Option<&mut T>;
278}
279
280impl<S: Struct> GetField for S {
281    fn get_field<T: Reflect>(&self, name: &str) -> Option<&T> {
282        self.field(name)
283            .and_then(|value| value.try_downcast_ref::<T>())
284    }
285
286    fn get_field_mut<T: Reflect>(&mut self, name: &str) -> Option<&mut T> {
287        self.field_mut(name)
288            .and_then(|value| value.try_downcast_mut::<T>())
289    }
290}
291
292impl GetField for dyn Struct {
293    fn get_field<T: Reflect>(&self, name: &str) -> Option<&T> {
294        self.field(name)
295            .and_then(|value| value.try_downcast_ref::<T>())
296    }
297
298    fn get_field_mut<T: Reflect>(&mut self, name: &str) -> Option<&mut T> {
299        self.field_mut(name)
300            .and_then(|value| value.try_downcast_mut::<T>())
301    }
302}
303
304/// A struct type which allows fields to be added at runtime.
305#[derive(Default)]
306pub struct DynamicStruct {
307    represented_type: Option<&'static TypeInfo>,
308    fields: Vec<Box<dyn PartialReflect>>,
309    field_names: Vec<Cow<'static, str>>,
310    field_indices: HashMap<Cow<'static, str>, usize>,
311}
312
313impl DynamicStruct {
314    /// Sets the [type] to be represented by this `DynamicStruct`.
315    ///
316    /// # Panics
317    ///
318    /// Panics if the given [type] is not a [`TypeInfo::Struct`].
319    ///
320    /// [type]: TypeInfo
321    pub fn set_represented_type(&mut self, represented_type: Option<&'static TypeInfo>) {
322        if let Some(represented_type) = represented_type {
323            assert!(
324                matches!(represented_type, TypeInfo::Struct(_)),
325                "expected TypeInfo::Struct but received: {represented_type:?}"
326            );
327        }
328
329        self.represented_type = represented_type;
330    }
331
332    /// Inserts a field named `name` with value `value` into the struct.
333    ///
334    /// If the field already exists, it is overwritten.
335    pub fn insert_boxed<'a>(
336        &mut self,
337        name: impl Into<Cow<'a, str>>,
338        field: Box<dyn PartialReflect>,
339    ) {
340        let name: Cow<str> = name.into();
341        if let Some(index) = self.field_indices.get(&name) {
342            self.fields[*index] = field;
343        } else {
344            self.fields.push(field);
345            self.field_indices
346                .insert(Cow::Owned(name.clone().into_owned()), self.fields.len() - 1);
347            self.field_names.push(Cow::Owned(name.into_owned()));
348        }
349    }
350
351    /// Inserts a field named `name` with the typed value `value` into the struct.
352    ///
353    /// If the field already exists, it is overwritten.
354    pub fn insert<'a, T: PartialReflect>(&mut self, name: impl Into<Cow<'a, str>>, field: T) {
355        self.insert_boxed(name, Box::new(field));
356    }
357
358    /// Removes a field at `index`.
359    pub fn remove_at(
360        &mut self,
361        index: usize,
362    ) -> Option<(Cow<'static, str>, Box<dyn PartialReflect>)> {
363        let mut i: usize = 0;
364        let mut extract = self.field_names.extract_if(0..self.field_names.len(), |n| {
365            let mut result = false;
366            if i == index {
367                self.field_indices
368                    .remove(n)
369                    .expect("Invalid name for `field_indices.remove(name)`");
370                result = true;
371            } else if i > index {
372                *self
373                    .field_indices
374                    .get_mut(n)
375                    .expect("Invalid name for `field_indices.get_mut(name)`") -= 1;
376            }
377            i += 1;
378            result
379        });
380
381        let name = extract
382            .nth(0)
383            .expect("Invalid index for `extract.nth(index)`");
384        extract.for_each(drop); // Fully evaluate the rest of the iterator, so we don't short-circuit the extract.
385
386        Some((name, self.fields.remove(index)))
387    }
388
389    /// Removes the first field that satisfies the given predicate, `f`.
390    pub fn remove_if<F>(&mut self, mut f: F) -> Option<(Cow<'static, str>, Box<dyn PartialReflect>)>
391    where
392        F: FnMut((&str, &dyn PartialReflect)) -> bool,
393    {
394        if let Some(index) = self
395            .field_names
396            .iter()
397            .zip(self.fields.iter())
398            .position(|(name, field)| f((name.as_ref(), field.as_ref())))
399        {
400            self.remove_at(index)
401        } else {
402            None
403        }
404    }
405
406    /// Removes a field by `name`.
407    pub fn remove_by_name(
408        &mut self,
409        name: &str,
410    ) -> Option<(Cow<'static, str>, Box<dyn PartialReflect>)> {
411        if let Some(index) = self.index_of_name(name) {
412            self.remove_at(index)
413        } else {
414            None
415        }
416    }
417}
418
419impl Struct for DynamicStruct {
420    #[inline]
421    fn field(&self, name: &str) -> Option<&dyn PartialReflect> {
422        self.field_indices
423            .get(name)
424            .map(|index| &*self.fields[*index])
425    }
426
427    #[inline]
428    fn field_mut(&mut self, name: &str) -> Option<&mut dyn PartialReflect> {
429        if let Some(index) = self.index_of_name(name) {
430            Some(self.fields[index].as_mut())
431        } else {
432            None
433        }
434    }
435
436    #[inline]
437    fn field_at(&self, index: usize) -> Option<&dyn PartialReflect> {
438        self.fields.get(index).map(AsRef::as_ref)
439    }
440
441    #[inline]
442    fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn PartialReflect> {
443        self.fields.get_mut(index).map(AsMut::as_mut)
444    }
445
446    #[inline]
447    fn name_at(&self, index: usize) -> Option<&str> {
448        self.field_names.get(index).map(AsRef::as_ref)
449    }
450
451    /// Gets the index of the field with the given name.
452    #[inline]
453    fn index_of_name(&self, name: &str) -> Option<usize> {
454        self.field_indices.get(name).copied()
455    }
456
457    #[inline]
458    fn field_len(&self) -> usize {
459        self.fields.len()
460    }
461
462    #[inline]
463    fn iter_fields(&self) -> FieldIter<'_> {
464        FieldIter::new(self)
465    }
466}
467
468impl PartialReflect for DynamicStruct {
469    #[inline]
470    fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
471        self.represented_type
472    }
473
474    #[inline]
475    fn into_partial_reflect(self: Box<Self>) -> Box<dyn PartialReflect> {
476        self
477    }
478
479    #[inline]
480    fn as_partial_reflect(&self) -> &dyn PartialReflect {
481        self
482    }
483
484    #[inline]
485    fn as_partial_reflect_mut(&mut self) -> &mut dyn PartialReflect {
486        self
487    }
488
489    fn try_into_reflect(self: Box<Self>) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>> {
490        Err(self)
491    }
492    fn try_as_reflect(&self) -> Option<&dyn Reflect> {
493        None
494    }
495    fn try_as_reflect_mut(&mut self) -> Option<&mut dyn Reflect> {
496        None
497    }
498
499    fn try_apply(&mut self, value: &dyn PartialReflect) -> Result<(), ApplyError> {
500        let struct_value = value.reflect_ref().as_struct()?;
501
502        for (name, value) in struct_value {
503            if let Some(v) = self.field_mut(name) {
504                v.try_apply(value)?;
505            }
506        }
507
508        Ok(())
509    }
510
511    #[inline]
512    fn reflect_kind(&self) -> ReflectKind {
513        ReflectKind::Struct
514    }
515
516    #[inline]
517    fn reflect_ref(&self) -> ReflectRef<'_> {
518        ReflectRef::Struct(self)
519    }
520
521    #[inline]
522    fn reflect_mut(&mut self) -> ReflectMut<'_> {
523        ReflectMut::Struct(self)
524    }
525
526    #[inline]
527    fn reflect_owned(self: Box<Self>) -> ReflectOwned {
528        ReflectOwned::Struct(self)
529    }
530
531    fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option<bool> {
532        struct_partial_eq(self, value)
533    }
534
535    fn reflect_partial_cmp(&self, value: &dyn PartialReflect) -> Option<::core::cmp::Ordering> {
536        struct_partial_cmp(self, value)
537    }
538
539    fn debug(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
540        write!(f, "DynamicStruct(")?;
541        struct_debug(self, f)?;
542        write!(f, ")")
543    }
544
545    #[inline]
546    fn is_dynamic(&self) -> bool {
547        true
548    }
549}
550
551impl_type_path!((in bevy_reflect) DynamicStruct);
552
553impl Debug for DynamicStruct {
554    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
555        self.debug(f)
556    }
557}
558
559impl<'a, N> FromIterator<(N, Box<dyn PartialReflect>)> for DynamicStruct
560where
561    N: Into<Cow<'a, str>>,
562{
563    /// Create a dynamic struct that doesn't represent a type from the
564    /// field name, field value pairs.
565    fn from_iter<I: IntoIterator<Item = (N, Box<dyn PartialReflect>)>>(fields: I) -> Self {
566        let mut dynamic_struct = Self::default();
567        for (name, value) in fields.into_iter() {
568            dynamic_struct.insert_boxed(name, value);
569        }
570        dynamic_struct
571    }
572}
573
574impl IntoIterator for DynamicStruct {
575    type Item = (Cow<'static, str>, Box<dyn PartialReflect>);
576    type IntoIter = core::iter::Zip<
577        alloc::vec::IntoIter<Cow<'static, str>>,
578        alloc::vec::IntoIter<Box<dyn PartialReflect>>,
579    >;
580
581    fn into_iter(self) -> Self::IntoIter {
582        self.field_names.into_iter().zip(self.fields)
583    }
584}
585
586impl<'a> IntoIterator for &'a DynamicStruct {
587    type Item = (&'a str, &'a dyn PartialReflect);
588    type IntoIter = FieldIter<'a>;
589
590    fn into_iter(self) -> Self::IntoIter {
591        self.iter_fields()
592    }
593}
594
595/// Compares a [`Struct`] with a [`PartialReflect`] value.
596///
597/// Returns true if and only if all of the following are true:
598/// - `b` is a struct;
599/// - For each field in `a`, `b` contains a field with the same name and
600///   [`PartialReflect::reflect_partial_eq`] returns `Some(true)` for the two field
601///   values.
602///
603/// Returns [`None`] if the comparison couldn't even be performed.
604#[inline(never)]
605pub fn struct_partial_eq(a: &dyn Struct, b: &dyn PartialReflect) -> Option<bool> {
606    let ReflectRef::Struct(struct_value) = b.reflect_ref() else {
607        return Some(false);
608    };
609
610    if a.field_len() != struct_value.field_len() {
611        return Some(false);
612    }
613
614    for (name, value) in struct_value {
615        if let Some(field_value) = a.field(name) {
616            let eq_result = field_value.reflect_partial_eq(value);
617            if let failed @ (Some(false) | None) = eq_result {
618                return failed;
619            }
620        } else {
621            return Some(false);
622        }
623    }
624
625    Some(true)
626}
627
628/// Lexicographically compares two [`Struct`] values and returns their ordering.
629///
630/// Returns [`None`] if the comparison couldn't be performed (e.g., kinds mismatch
631/// or an element comparison returns `None`).
632#[inline(never)]
633pub fn struct_partial_cmp(a: &dyn Struct, b: &dyn PartialReflect) -> Option<::core::cmp::Ordering> {
634    let ReflectRef::Struct(struct_value) = b.reflect_ref() else {
635        return None;
636    };
637
638    if a.field_len() != struct_value.field_len() {
639        return None;
640    }
641
642    // Delegate detailed field-name-aware comparison to shared helper
643    partial_cmp_by_field_names(
644        a.field_len(),
645        |i| a.name_at(i),
646        |i| a.field_at(i),
647        |i| struct_value.name_at(i),
648        |i| struct_value.field_at(i),
649        |name| struct_value.field(name),
650    )
651}
652
653/// Compare two sets of named fields. `field_len` should be equal.
654///
655/// Tries best to:
656/// 1. when used on concrete types of actually same type, should be compatible
657///    with derived `PartialOrd` implementations.
658/// 2. compatible with `reflect_partial_eq`: when `reflect_partial_eq(a, b) = Some(true)`,
659///    then `partial_cmp(a, b) = Some(Ordering::Equal)`.
660/// 3. when used on dynamic types, provide a consistent ordering:
661///    see example `crate::tests:reflect_partial_cmp_struct_named_field_reorder`
662pub(crate) fn partial_cmp_by_field_names<'a, NA, FA, NB, FB, FBY>(
663    field_len: usize,
664    name_at_a: NA,
665    field_at_a: FA,
666    name_at_b: NB,
667    field_at_b_index: FB,
668    field_b_by_name: FBY,
669) -> Option<::core::cmp::Ordering>
670where
671    NA: Fn(usize) -> Option<&'a str>,
672    FA: Fn(usize) -> Option<&'a dyn PartialReflect>,
673    NB: Fn(usize) -> Option<&'a str>,
674    FB: Fn(usize) -> Option<&'a dyn PartialReflect>,
675    FBY: Fn(&str) -> Option<&'a dyn PartialReflect>,
676{
677    use ::core::cmp::Ordering;
678
679    let mut same_field_order = true;
680    for i in 0..field_len {
681        if name_at_a(i) != name_at_b(i) {
682            same_field_order = false;
683            break;
684        }
685    }
686
687    if same_field_order {
688        for i in 0..field_len {
689            let a_val = field_at_a(i).unwrap();
690            let b_val = field_at_b_index(i).unwrap();
691            match a_val.reflect_partial_cmp(b_val) {
692                None => return None,
693                Some(Ordering::Equal) => continue,
694                Some(ord) => return Some(ord),
695            }
696        }
697        return Some(Ordering::Equal);
698    }
699
700    let mut all_less_equal = true;
701    let mut all_greater_equal = true;
702    let mut all_equal = true;
703
704    for i in 0..field_len {
705        let field_name = name_at_a(i).unwrap();
706        let a_val = field_at_a(i).unwrap();
707        let b_val = field_b_by_name(field_name)?;
708        match a_val.reflect_partial_cmp(b_val) {
709            None => return None,
710            Some(::core::cmp::Ordering::Less) => {
711                all_greater_equal = false;
712                all_equal = false;
713            }
714            Some(::core::cmp::Ordering::Greater) => {
715                all_less_equal = false;
716                all_equal = false;
717            }
718            Some(::core::cmp::Ordering::Equal) => {}
719        }
720    }
721
722    if all_equal {
723        Some(::core::cmp::Ordering::Equal)
724    } else if all_less_equal {
725        Some(::core::cmp::Ordering::Less)
726    } else if all_greater_equal {
727        Some(::core::cmp::Ordering::Greater)
728    } else {
729        None
730    }
731}
732
733/// The default debug formatter for [`Struct`] types.
734///
735/// # Example
736/// ```
737/// use bevy_reflect::Reflect;
738/// #[derive(Reflect)]
739/// struct MyStruct {
740///   foo: usize
741/// }
742///
743/// let my_struct: &dyn Reflect = &MyStruct { foo: 123 };
744/// println!("{:#?}", my_struct);
745///
746/// // Output:
747///
748/// // MyStruct {
749/// //   foo: 123,
750/// // }
751/// ```
752#[inline]
753pub fn struct_debug(dyn_struct: &dyn Struct, f: &mut Formatter<'_>) -> core::fmt::Result {
754    let mut debug = f.debug_struct(
755        dyn_struct
756            .get_represented_type_info()
757            .map(TypeInfo::type_path)
758            .unwrap_or("_"),
759    );
760    for field_index in 0..dyn_struct.field_len() {
761        let field = dyn_struct.field_at(field_index).unwrap();
762        debug.field(
763            dyn_struct.name_at(field_index).unwrap(),
764            &field as &dyn Debug,
765        );
766    }
767    debug.finish()
768}
769
770#[cfg(test)]
771mod tests {
772    use crate::{structs::*, *};
773    use alloc::borrow::ToOwned;
774
775    #[derive(Reflect, Default)]
776    struct MyStruct {
777        a: (),
778        b: (),
779        c: (),
780    }
781
782    #[derive(Reflect, Default)]
783    struct OtherStruct {
784        a: u32,
785        b: u64,
786        c: MyStruct,
787    }
788
789    #[test]
790    fn dynamic_struct_remove_at() {
791        let mut s = OtherStruct::default().to_dynamic_struct().unwrap();
792
793        assert_eq!(s.field_len(), 3);
794
795        let field_2 = s
796            .remove_at(1)
797            .expect("Invalid index for `s.remove_at(index)`");
798
799        assert_eq!(s.field_len(), 2);
800        assert_eq!(field_2.0, "b");
801
802        let field_3 = s
803            .remove_at(0)
804            .expect("Invalid index for `s.remove_at(index)`");
805
806        assert_eq!(s.field_len(), 1);
807        assert_eq!(field_3.0, "a");
808
809        let field_1 = s
810            .remove_at(0)
811            .expect("Invalid index for `s.remove_at(index)`");
812
813        assert_eq!(s.field_len(), 0);
814        assert_eq!(field_1.0, "c");
815    }
816
817    #[test]
818    fn dynamic_struct_remove_by_name() {
819        let mut s = OtherStruct::default().to_dynamic_struct().unwrap();
820
821        assert_eq!(s.field_len(), 3);
822
823        let field_3 = s
824            .remove_by_name("b")
825            .expect("Invalid name for `s.remove_by_name(name)`");
826
827        assert_eq!(s.field_len(), 2);
828        assert_eq!(field_3.0, "b");
829
830        let field_2 = s
831            .remove_by_name("c")
832            .expect("Invalid name for `s.remove_by_name(name)`");
833
834        assert_eq!(s.field_len(), 1);
835        assert_eq!(field_2.0, "c");
836
837        let field_1 = s
838            .remove_by_name("a")
839            .expect("Invalid name for `s.remove_by_name(name)`");
840
841        assert_eq!(s.field_len(), 0);
842        assert_eq!(field_1.0, "a");
843    }
844
845    #[test]
846    fn dynamic_struct_remove_if() {
847        let mut s = OtherStruct::default().to_dynamic_struct().unwrap();
848
849        assert_eq!(s.field_len(), 3);
850
851        let field_3_name = s
852            .name_at(2)
853            .expect("Invalid index for `s.field_at(index)`")
854            .to_owned();
855        let field_3 = s
856            .remove_if(|(name, _field)| name == field_3_name)
857            .expect("No valid name/field found for `s.remove_with(|(name, field)|{})");
858
859        assert_eq!(s.field_len(), 2);
860        assert_eq!(field_3.0, "c");
861    }
862
863    #[test]
864    fn dynamic_struct_remove_combo() {
865        let mut s = OtherStruct::default().to_dynamic_struct().unwrap();
866
867        assert_eq!(s.field_len(), 3);
868
869        let field_2 = s
870            .remove_at(
871                s.index_of_name("b")
872                    .expect("Invalid name for `s.index_of_name(name)`"),
873            )
874            .expect("Invalid index for `s.remove_at(index)`");
875
876        assert_eq!(s.field_len(), 2);
877        assert_eq!(field_2.0, "b");
878
879        let field_3_name = s
880            .name_at(1)
881            .expect("Invalid name for s.name_at(index)")
882            .to_owned();
883
884        let field_3 = s
885            .remove_by_name(field_3_name.as_ref())
886            .expect("Invalid name for `s.remove_by_name(name)`");
887
888        assert_eq!(s.field_len(), 1);
889        assert_eq!(field_3.0, "c");
890
891        let field_1_name = s
892            .name_at(0)
893            .expect("Invalid name for `s.name_at(name)`")
894            .to_owned();
895        let field_1 = s
896            .remove_if(|(name, _field)| name == field_1_name)
897            .expect("No valid name/field found for `s.remove_with(|(name, field)|{})`");
898
899        assert_eq!(s.field_len(), 0);
900        assert_eq!(field_1.0, "a");
901    }
902
903    #[test]
904    fn next_index_increment() {
905        let my_struct = MyStruct::default();
906        let mut iter = my_struct.iter_fields();
907        iter.index = iter.len() - 1;
908        let prev_index = iter.index;
909        assert!(iter.next().is_some());
910        assert_eq!(prev_index, iter.index - 1);
911
912        // When None we should no longer increase index
913        let prev_index = iter.index;
914        assert!(iter.next().is_none());
915        assert_eq!(prev_index, iter.index);
916        assert!(iter.next().is_none());
917        assert_eq!(prev_index, iter.index);
918    }
919}