bevy_reflect/
struct_trait.rs

1use crate::generics::impl_generic_info_methods;
2use crate::{
3    attributes::{impl_custom_attribute_methods, CustomAttributes},
4    type_info::impl_type_methods,
5    ApplyError, Generics, NamedField, PartialReflect, Reflect, ReflectKind, ReflectMut,
6    ReflectOwned, ReflectRef, Type, TypeInfo, TypePath,
7};
8use alloc::{borrow::Cow, boxed::Box, vec::Vec};
9use bevy_platform::collections::HashMap;
10use bevy_platform::sync::Arc;
11use bevy_reflect_derive::impl_type_path;
12use core::{
13    fmt::{Debug, Formatter},
14    slice::Iter,
15};
16
17/// A trait used to power [struct-like] operations via [reflection].
18///
19/// This trait uses the [`Reflect`] trait to allow implementors to have their fields
20/// be dynamically addressed by both name and index.
21///
22/// When using [`#[derive(Reflect)]`](derive@crate::Reflect) on a standard struct,
23/// this trait will be automatically implemented.
24/// This goes for [unit structs] as well.
25///
26/// # Example
27///
28/// ```
29/// use bevy_reflect::{PartialReflect, Reflect, Struct};
30///
31/// #[derive(Reflect)]
32/// struct Foo {
33///     bar: u32,
34/// }
35///
36/// let foo = Foo { bar: 123 };
37///
38/// assert_eq!(foo.field_len(), 1);
39/// assert_eq!(foo.name_at(0), Some("bar"));
40///
41/// let field: &dyn PartialReflect = foo.field("bar").unwrap();
42/// assert_eq!(field.try_downcast_ref::<u32>(), Some(&123));
43/// ```
44///
45/// [struct-like]: https://doc.rust-lang.org/book/ch05-01-defining-structs.html
46/// [reflection]: crate
47/// [unit structs]: https://doc.rust-lang.org/book/ch05-01-defining-structs.html#unit-like-structs-without-any-fields
48pub trait Struct: PartialReflect {
49    /// Returns a reference to the value of the field named `name` as a `&dyn
50    /// PartialReflect`.
51    fn field(&self, name: &str) -> Option<&dyn PartialReflect>;
52
53    /// Returns a mutable reference to the value of the field named `name` as a
54    /// `&mut dyn PartialReflect`.
55    fn field_mut(&mut self, name: &str) -> Option<&mut dyn PartialReflect>;
56
57    /// Returns a reference to the value of the field with index `index` as a
58    /// `&dyn PartialReflect`.
59    fn field_at(&self, index: usize) -> Option<&dyn PartialReflect>;
60
61    /// Returns a mutable reference to the value of the field with index `index`
62    /// as a `&mut dyn PartialReflect`.
63    fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn PartialReflect>;
64
65    /// Returns the name of the field with index `index`.
66    fn name_at(&self, index: usize) -> Option<&str>;
67
68    /// Returns the number of fields in the struct.
69    fn field_len(&self) -> usize;
70
71    /// Returns an iterator over the values of the reflectable fields for this struct.
72    fn iter_fields(&self) -> FieldIter;
73
74    /// Clones the struct into a [`DynamicStruct`].
75    #[deprecated(since = "0.16.0", note = "use `to_dynamic_struct` instead")]
76    fn clone_dynamic(&self) -> DynamicStruct {
77        self.to_dynamic_struct()
78    }
79
80    fn to_dynamic_struct(&self) -> DynamicStruct {
81        let mut dynamic_struct = DynamicStruct::default();
82        dynamic_struct.set_represented_type(self.get_represented_type_info());
83        for (i, value) in self.iter_fields().enumerate() {
84            dynamic_struct.insert_boxed(self.name_at(i).unwrap(), value.to_dynamic());
85        }
86        dynamic_struct
87    }
88
89    /// Will return `None` if [`TypeInfo`] is not available.
90    fn get_represented_struct_info(&self) -> Option<&'static StructInfo> {
91        self.get_represented_type_info()?.as_struct().ok()
92    }
93}
94
95/// A container for compile-time named struct info.
96#[derive(Clone, Debug)]
97pub struct StructInfo {
98    ty: Type,
99    generics: Generics,
100    fields: Box<[NamedField]>,
101    field_names: Box<[&'static str]>,
102    field_indices: HashMap<&'static str, usize>,
103    custom_attributes: Arc<CustomAttributes>,
104    #[cfg(feature = "documentation")]
105    docs: Option<&'static str>,
106}
107
108impl StructInfo {
109    /// Create a new [`StructInfo`].
110    ///
111    /// # Arguments
112    ///
113    /// * `fields`: The fields of this struct in the order they are defined
114    pub fn new<T: Reflect + TypePath>(fields: &[NamedField]) -> Self {
115        let field_indices = fields
116            .iter()
117            .enumerate()
118            .map(|(index, field)| (field.name(), index))
119            .collect::<HashMap<_, _>>();
120
121        let field_names = fields.iter().map(NamedField::name).collect();
122
123        Self {
124            ty: Type::of::<T>(),
125            generics: Generics::new(),
126            fields: fields.to_vec().into_boxed_slice(),
127            field_names,
128            field_indices,
129            custom_attributes: Arc::new(CustomAttributes::default()),
130            #[cfg(feature = "documentation")]
131            docs: None,
132        }
133    }
134
135    /// Sets the docstring for this struct.
136    #[cfg(feature = "documentation")]
137    pub fn with_docs(self, docs: Option<&'static str>) -> Self {
138        Self { docs, ..self }
139    }
140
141    /// Sets the custom attributes for this struct.
142    pub fn with_custom_attributes(self, custom_attributes: CustomAttributes) -> Self {
143        Self {
144            custom_attributes: Arc::new(custom_attributes),
145            ..self
146        }
147    }
148
149    /// A slice containing the names of all fields in order.
150    pub fn field_names(&self) -> &[&'static str] {
151        &self.field_names
152    }
153
154    /// Get the field with the given name.
155    pub fn field(&self, name: &str) -> Option<&NamedField> {
156        self.field_indices
157            .get(name)
158            .map(|index| &self.fields[*index])
159    }
160
161    /// Get the field at the given index.
162    pub fn field_at(&self, index: usize) -> Option<&NamedField> {
163        self.fields.get(index)
164    }
165
166    /// Get the index of the field with the given name.
167    pub fn index_of(&self, name: &str) -> Option<usize> {
168        self.field_indices.get(name).copied()
169    }
170
171    /// Iterate over the fields of this struct.
172    pub fn iter(&self) -> Iter<'_, NamedField> {
173        self.fields.iter()
174    }
175
176    /// The total number of fields in this struct.
177    pub fn field_len(&self) -> usize {
178        self.fields.len()
179    }
180
181    impl_type_methods!(ty);
182
183    /// The docstring of this struct, if any.
184    #[cfg(feature = "documentation")]
185    pub fn docs(&self) -> Option<&'static str> {
186        self.docs
187    }
188
189    impl_custom_attribute_methods!(self.custom_attributes, "struct");
190
191    impl_generic_info_methods!(generics);
192}
193
194/// An iterator over the field values of a struct.
195pub struct FieldIter<'a> {
196    pub(crate) struct_val: &'a dyn Struct,
197    pub(crate) index: usize,
198}
199
200impl<'a> FieldIter<'a> {
201    pub fn new(value: &'a dyn Struct) -> Self {
202        FieldIter {
203            struct_val: value,
204            index: 0,
205        }
206    }
207}
208
209impl<'a> Iterator for FieldIter<'a> {
210    type Item = &'a dyn PartialReflect;
211
212    fn next(&mut self) -> Option<Self::Item> {
213        let value = self.struct_val.field_at(self.index);
214        self.index += value.is_some() as usize;
215        value
216    }
217
218    fn size_hint(&self) -> (usize, Option<usize>) {
219        let size = self.struct_val.field_len();
220        (size, Some(size))
221    }
222}
223
224impl<'a> ExactSizeIterator for FieldIter<'a> {}
225
226/// A convenience trait which combines fetching and downcasting of struct
227/// fields.
228///
229/// # Example
230///
231/// ```
232/// use bevy_reflect::{GetField, Reflect};
233///
234/// #[derive(Reflect)]
235/// struct Foo {
236///     bar: String,
237/// }
238///
239/// # fn main() {
240/// let mut foo = Foo { bar: "Hello, world!".to_string() };
241///
242/// foo.get_field_mut::<String>("bar").unwrap().truncate(5);
243/// assert_eq!(foo.get_field::<String>("bar"), Some(&"Hello".to_string()));
244/// # }
245/// ```
246pub trait GetField {
247    /// Returns a reference to the value of the field named `name`, downcast to
248    /// `T`.
249    fn get_field<T: Reflect>(&self, name: &str) -> Option<&T>;
250
251    /// Returns a mutable reference to the value of the field named `name`,
252    /// downcast to `T`.
253    fn get_field_mut<T: Reflect>(&mut self, name: &str) -> Option<&mut T>;
254}
255
256impl<S: Struct> GetField for S {
257    fn get_field<T: Reflect>(&self, name: &str) -> Option<&T> {
258        self.field(name)
259            .and_then(|value| value.try_downcast_ref::<T>())
260    }
261
262    fn get_field_mut<T: Reflect>(&mut self, name: &str) -> Option<&mut T> {
263        self.field_mut(name)
264            .and_then(|value| value.try_downcast_mut::<T>())
265    }
266}
267
268impl GetField for dyn Struct {
269    fn get_field<T: Reflect>(&self, name: &str) -> Option<&T> {
270        self.field(name)
271            .and_then(|value| value.try_downcast_ref::<T>())
272    }
273
274    fn get_field_mut<T: Reflect>(&mut self, name: &str) -> Option<&mut T> {
275        self.field_mut(name)
276            .and_then(|value| value.try_downcast_mut::<T>())
277    }
278}
279
280/// A struct type which allows fields to be added at runtime.
281#[derive(Default)]
282pub struct DynamicStruct {
283    represented_type: Option<&'static TypeInfo>,
284    fields: Vec<Box<dyn PartialReflect>>,
285    field_names: Vec<Cow<'static, str>>,
286    field_indices: HashMap<Cow<'static, str>, usize>,
287}
288
289impl DynamicStruct {
290    /// Sets the [type] to be represented by this `DynamicStruct`.
291    ///
292    /// # Panics
293    ///
294    /// Panics if the given [type] is not a [`TypeInfo::Struct`].
295    ///
296    /// [type]: TypeInfo
297    pub fn set_represented_type(&mut self, represented_type: Option<&'static TypeInfo>) {
298        if let Some(represented_type) = represented_type {
299            assert!(
300                matches!(represented_type, TypeInfo::Struct(_)),
301                "expected TypeInfo::Struct but received: {:?}",
302                represented_type
303            );
304        }
305
306        self.represented_type = represented_type;
307    }
308
309    /// Inserts a field named `name` with value `value` into the struct.
310    ///
311    /// If the field already exists, it is overwritten.
312    pub fn insert_boxed<'a>(
313        &mut self,
314        name: impl Into<Cow<'a, str>>,
315        value: Box<dyn PartialReflect>,
316    ) {
317        let name: Cow<str> = name.into();
318        if let Some(index) = self.field_indices.get(&name) {
319            self.fields[*index] = value;
320        } else {
321            self.fields.push(value);
322            self.field_indices
323                .insert(Cow::Owned(name.clone().into_owned()), self.fields.len() - 1);
324            self.field_names.push(Cow::Owned(name.into_owned()));
325        }
326    }
327
328    /// Inserts a field named `name` with the typed value `value` into the struct.
329    ///
330    /// If the field already exists, it is overwritten.
331    pub fn insert<'a, T: PartialReflect>(&mut self, name: impl Into<Cow<'a, str>>, value: T) {
332        self.insert_boxed(name, Box::new(value));
333    }
334
335    /// Gets the index of the field with the given name.
336    pub fn index_of(&self, name: &str) -> Option<usize> {
337        self.field_indices.get(name).copied()
338    }
339}
340
341impl Struct for DynamicStruct {
342    #[inline]
343    fn field(&self, name: &str) -> Option<&dyn PartialReflect> {
344        self.field_indices
345            .get(name)
346            .map(|index| &*self.fields[*index])
347    }
348
349    #[inline]
350    fn field_mut(&mut self, name: &str) -> Option<&mut dyn PartialReflect> {
351        if let Some(index) = self.field_indices.get(name) {
352            Some(&mut *self.fields[*index])
353        } else {
354            None
355        }
356    }
357
358    #[inline]
359    fn field_at(&self, index: usize) -> Option<&dyn PartialReflect> {
360        self.fields.get(index).map(|value| &**value)
361    }
362
363    #[inline]
364    fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn PartialReflect> {
365        self.fields.get_mut(index).map(|value| &mut **value)
366    }
367
368    #[inline]
369    fn name_at(&self, index: usize) -> Option<&str> {
370        self.field_names.get(index).map(AsRef::as_ref)
371    }
372
373    #[inline]
374    fn field_len(&self) -> usize {
375        self.fields.len()
376    }
377
378    #[inline]
379    fn iter_fields(&self) -> FieldIter {
380        FieldIter {
381            struct_val: self,
382            index: 0,
383        }
384    }
385}
386
387impl PartialReflect for DynamicStruct {
388    #[inline]
389    fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
390        self.represented_type
391    }
392
393    #[inline]
394    fn into_partial_reflect(self: Box<Self>) -> Box<dyn PartialReflect> {
395        self
396    }
397
398    #[inline]
399    fn as_partial_reflect(&self) -> &dyn PartialReflect {
400        self
401    }
402
403    #[inline]
404    fn as_partial_reflect_mut(&mut self) -> &mut dyn PartialReflect {
405        self
406    }
407
408    fn try_into_reflect(self: Box<Self>) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>> {
409        Err(self)
410    }
411    fn try_as_reflect(&self) -> Option<&dyn Reflect> {
412        None
413    }
414    fn try_as_reflect_mut(&mut self) -> Option<&mut dyn Reflect> {
415        None
416    }
417
418    fn try_apply(&mut self, value: &dyn PartialReflect) -> Result<(), ApplyError> {
419        let struct_value = value.reflect_ref().as_struct()?;
420
421        for (i, value) in struct_value.iter_fields().enumerate() {
422            let name = struct_value.name_at(i).unwrap();
423            if let Some(v) = self.field_mut(name) {
424                v.try_apply(value)?;
425            }
426        }
427
428        Ok(())
429    }
430
431    #[inline]
432    fn reflect_kind(&self) -> ReflectKind {
433        ReflectKind::Struct
434    }
435
436    #[inline]
437    fn reflect_ref(&self) -> ReflectRef {
438        ReflectRef::Struct(self)
439    }
440
441    #[inline]
442    fn reflect_mut(&mut self) -> ReflectMut {
443        ReflectMut::Struct(self)
444    }
445
446    #[inline]
447    fn reflect_owned(self: Box<Self>) -> ReflectOwned {
448        ReflectOwned::Struct(self)
449    }
450
451    fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option<bool> {
452        struct_partial_eq(self, value)
453    }
454
455    fn debug(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
456        write!(f, "DynamicStruct(")?;
457        struct_debug(self, f)?;
458        write!(f, ")")
459    }
460
461    #[inline]
462    fn is_dynamic(&self) -> bool {
463        true
464    }
465}
466
467impl_type_path!((in bevy_reflect) DynamicStruct);
468
469impl Debug for DynamicStruct {
470    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
471        self.debug(f)
472    }
473}
474
475impl<'a, N> FromIterator<(N, Box<dyn PartialReflect>)> for DynamicStruct
476where
477    N: Into<Cow<'a, str>>,
478{
479    /// Create a dynamic struct that doesn't represent a type from the
480    /// field name, field value pairs.
481    fn from_iter<I: IntoIterator<Item = (N, Box<dyn PartialReflect>)>>(fields: I) -> Self {
482        let mut dynamic_struct = Self::default();
483        for (name, value) in fields.into_iter() {
484            dynamic_struct.insert_boxed(name, value);
485        }
486        dynamic_struct
487    }
488}
489
490impl IntoIterator for DynamicStruct {
491    type Item = Box<dyn PartialReflect>;
492    type IntoIter = alloc::vec::IntoIter<Self::Item>;
493
494    fn into_iter(self) -> Self::IntoIter {
495        self.fields.into_iter()
496    }
497}
498
499impl<'a> IntoIterator for &'a DynamicStruct {
500    type Item = &'a dyn PartialReflect;
501    type IntoIter = FieldIter<'a>;
502
503    fn into_iter(self) -> Self::IntoIter {
504        self.iter_fields()
505    }
506}
507
508/// Compares a [`Struct`] with a [`PartialReflect`] value.
509///
510/// Returns true if and only if all of the following are true:
511/// - `b` is a struct;
512/// - For each field in `a`, `b` contains a field with the same name and
513///   [`PartialReflect::reflect_partial_eq`] returns `Some(true)` for the two field
514///   values.
515///
516/// Returns [`None`] if the comparison couldn't even be performed.
517#[inline]
518pub fn struct_partial_eq<S: Struct + ?Sized>(a: &S, b: &dyn PartialReflect) -> Option<bool> {
519    let ReflectRef::Struct(struct_value) = b.reflect_ref() else {
520        return Some(false);
521    };
522
523    if a.field_len() != struct_value.field_len() {
524        return Some(false);
525    }
526
527    for (i, value) in struct_value.iter_fields().enumerate() {
528        let name = struct_value.name_at(i).unwrap();
529        if let Some(field_value) = a.field(name) {
530            let eq_result = field_value.reflect_partial_eq(value);
531            if let failed @ (Some(false) | None) = eq_result {
532                return failed;
533            }
534        } else {
535            return Some(false);
536        }
537    }
538
539    Some(true)
540}
541
542/// The default debug formatter for [`Struct`] types.
543///
544/// # Example
545/// ```
546/// use bevy_reflect::Reflect;
547/// #[derive(Reflect)]
548/// struct MyStruct {
549///   foo: usize
550/// }
551///
552/// let my_struct: &dyn Reflect = &MyStruct { foo: 123 };
553/// println!("{:#?}", my_struct);
554///
555/// // Output:
556///
557/// // MyStruct {
558/// //   foo: 123,
559/// // }
560/// ```
561#[inline]
562pub fn struct_debug(dyn_struct: &dyn Struct, f: &mut Formatter<'_>) -> core::fmt::Result {
563    let mut debug = f.debug_struct(
564        dyn_struct
565            .get_represented_type_info()
566            .map(TypeInfo::type_path)
567            .unwrap_or("_"),
568    );
569    for field_index in 0..dyn_struct.field_len() {
570        let field = dyn_struct.field_at(field_index).unwrap();
571        debug.field(
572            dyn_struct.name_at(field_index).unwrap(),
573            &field as &dyn Debug,
574        );
575    }
576    debug.finish()
577}
578
579#[cfg(test)]
580mod tests {
581    use crate::*;
582    #[derive(Reflect, Default)]
583    struct MyStruct {
584        a: (),
585        b: (),
586        c: (),
587    }
588    #[test]
589    fn next_index_increment() {
590        let my_struct = MyStruct::default();
591        let mut iter = my_struct.iter_fields();
592        iter.index = iter.len() - 1;
593        let prev_index = iter.index;
594        assert!(iter.next().is_some());
595        assert_eq!(prev_index, iter.index - 1);
596
597        // When None we should no longer increase index
598        let prev_index = iter.index;
599        assert!(iter.next().is_none());
600        assert_eq!(prev_index, iter.index);
601        assert!(iter.next().is_none());
602        assert_eq!(prev_index, iter.index);
603    }
604}