bevy_reflect_derive/
serialization.rs

1use crate::{
2    derive_data::StructField,
3    field_attributes::{DefaultBehavior, ReflectIgnoreBehavior},
4};
5use bevy_macro_utils::fq_std::FQDefault;
6use quote::quote;
7use std::collections::HashMap;
8use syn::{spanned::Spanned, Path};
9
10type ReflectionIndex = usize;
11
12/// Collected serialization data used to generate a `SerializationData` type.
13pub(crate) struct SerializationDataDef {
14    /// Maps a field's _reflection_ index to its [`SkippedFieldDef`] if marked as `#[reflect(skip_serializing)]`.
15    skipped: HashMap<ReflectionIndex, SkippedFieldDef>,
16}
17
18impl SerializationDataDef {
19    /// Attempts to create a new `SerializationDataDef` from the given collection of fields.
20    ///
21    /// Returns `Ok(Some(data))` if there are any fields needing to be skipped during serialization.
22    /// Otherwise, returns `Ok(None)`.
23    pub fn new(
24        fields: &[StructField<'_>],
25        bevy_reflect_path: &Path,
26    ) -> Result<Option<Self>, syn::Error> {
27        let mut skipped = <HashMap<_, _>>::default();
28
29        for field in fields {
30            match field.attrs.ignore {
31                ReflectIgnoreBehavior::IgnoreSerialization => {
32                    skipped.insert(
33                        field.reflection_index.ok_or_else(|| {
34                            syn::Error::new(
35                                field.data.span(),
36                                "internal error: field is missing a reflection index",
37                            )
38                        })?,
39                        SkippedFieldDef::new(field, bevy_reflect_path)?,
40                    );
41                }
42                _ => continue,
43            }
44        }
45
46        if skipped.is_empty() {
47            Ok(None)
48        } else {
49            Ok(Some(Self { skipped }))
50        }
51    }
52
53    /// Returns a `TokenStream` containing an initialized `SerializationData` type.
54    pub fn as_serialization_data(&self, bevy_reflect_path: &Path) -> proc_macro2::TokenStream {
55        let fields =
56            self.skipped
57                .iter()
58                .map(|(reflection_index, SkippedFieldDef { default_fn })| {
59                    quote! {(
60                        #reflection_index,
61                        #bevy_reflect_path::serde::SkippedField::new(#default_fn)
62                    )}
63                });
64        quote! {
65            #bevy_reflect_path::serde::SerializationData::new(
66                ::core::iter::IntoIterator::into_iter([#(#fields),*])
67            )
68        }
69    }
70}
71
72/// Collected field data used to generate a `SkippedField` type.
73pub(crate) struct SkippedFieldDef {
74    /// The default function for this field.
75    ///
76    /// This is of type `fn() -> Box<dyn Reflect>`.
77    default_fn: proc_macro2::TokenStream,
78}
79
80impl SkippedFieldDef {
81    pub fn new(field: &StructField<'_>, bevy_reflect_path: &Path) -> Result<Self, syn::Error> {
82        let ty = &field.data.ty;
83
84        let default_fn = match &field.attrs.default {
85            DefaultBehavior::Func(func) => quote! {
86              || { #bevy_reflect_path::__macro_exports::alloc_utils::Box::new(#func()) }
87            },
88            _ => quote! {
89              || { #bevy_reflect_path::__macro_exports::alloc_utils::Box::new(<#ty as #FQDefault>::default()) }
90            },
91        };
92
93        Ok(Self { default_fn })
94    }
95}