mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 12:43:34 +00:00
bevy_reflect: Fix ignored/skipped field order (#7575)
# Objective Fixes #5101 Alternative to #6511 ## Solution Corrected the behavior for ignored fields in `FromReflect`, which was previously using the incorrect field indexes. Similarly, fields marked with `#[reflect(skip_serializing)]` no longer break when using `FromReflect` after deserialization. This was done by modifying `SerializationData` to store a function pointer that can later be used to generate a default instance of the skipped field during deserialization. The function pointer points to a function generated by the derive macro using the behavior designated by `#[reflect(default)]` (or just `Default` if none provided). The entire output of the macro is now wrapped in an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) which keeps this behavior hygienic. #### Rationale The biggest downside to this approach is that it requires fields marked `#[reflect(skip_serializing)]` to provide the ability to create a default instance— either via a `Default` impl or by specifying a custom one. While this isn't great, I think it might be justified by the fact that we really need to create this value when using `FromReflect` on a deserialized object. And we need to do this _during_ deserialization because after that (at least for tuples and tuple structs) we lose information about which field is which: _"is the value at index 1 in this `DynamicTupleStruct` the actual value for index 1 or is it really the value for index 2 since index 1 is skippable...?"_ #### Alternatives An alternative would be to store `Option<Box<dyn Reflect>>` within `DynamicTuple` and `DynamicTupleStruct` instead of just `Box<dyn Reflect>`. This would allow us to insert "empty"/"missing" fields during deserialization, thus saving the positional information of the skipped fields. However, this may require changing the API of `Tuple` and `TupleStruct` such that they can account for their dynamic counterparts returning `None` for a skipped field. In practice this would probably mean exposing the `Option`-ness of the dynamics onto implementors via methods like `Tuple::drain` or `TupleStruct::field`. Personally, I think requiring `Default` would be better than muddying up the API to account for these special cases. But I'm open to trying out this other approach if the community feels that it's better. --- ## Changelog ### Public Changes #### Fixed - The behaviors of `#[reflect(ignore)]` and `#[reflect(skip_serializing)]` are no longer dependent on field order #### Changed - Fields marked with `#[reflect(skip_serializing)]` now need to either implement `Default` or specify a custom default function using `#[reflect(default = "path::to::some_func")]` - Deserializing a type with fields marked `#[reflect(skip_serializing)]` will now include that field initialized to its specified default value - `SerializationData::new` now takes the new `SkippedField` struct along with the skipped field index - Renamed `SerializationData::is_ignored_field` to `SerializationData::is_field_skipped` #### Added - Added `SkippedField` struct - Added methods `SerializationData::generate_default` and `SerializationData::iter_skipped` ### Internal Changes #### Changed - Replaced `members_to_serialization_denylist` and `BitSet<u32>` with `SerializationDataDef` - The `Reflect` derive is more hygienic as it now outputs within an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) - `StructField::index` has been split up into `StructField::declaration_index` and `StructField::reflection_index` #### Removed - Removed `bitset` dependency ## Migration Guide * Fields marked `#[reflect(skip_serializing)]` now must implement `Default` or specify a custom default function with `#[reflect(default = "path::to::some_func")]` ```rust #[derive(Reflect)] struct MyStruct { #[reflect(skip_serializing)] #[reflect(default = "get_foo_default")] foo: Foo, // <- `Foo` does not impl `Default` so requires a custom function #[reflect(skip_serializing)] bar: Bar, // <- `Bar` impls `Default` } #[derive(Reflect)] struct Foo(i32); #[derive(Reflect, Default)] struct Bar(i32); fn get_foo_default() -> Foo { Foo(123) } ``` * `SerializationData::new` has been changed to expect an iterator of `(usize, SkippedField)` rather than one of just `usize` ```rust // BEFORE SerializationData::new([0, 3].into_iter()); // AFTER SerializationData::new([ (0, SkippedField::new(field_0_default_fn)), (3, SkippedField::new(field_3_default_fn)), ].into_iter()); ``` * `Serialization::is_ignored_field` has been renamed to `Serialization::is_field_skipped` * Fields marked `#[reflect(skip_serializing)]` are now included in deserialization output. This may affect logic that expected those fields to be absent.
This commit is contained in:
parent
8efcbf3e4f
commit
60773e6787
16 changed files with 610 additions and 299 deletions
|
@ -23,4 +23,3 @@ syn = { version = "2.0", features = ["full"] }
|
|||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
uuid = { version = "1.1", features = ["v4"] }
|
||||
bit-set = "0.5.2"
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use crate::container_attributes::{FromReflectAttrs, ReflectTraits};
|
||||
use crate::field_attributes::{parse_field_attrs, ReflectFieldAttr};
|
||||
use crate::type_path::parse_path_no_leading_colon;
|
||||
use crate::utility::{members_to_serialization_denylist, StringExpr, WhereClauseOptions};
|
||||
use bit_set::BitSet;
|
||||
use crate::utility::{StringExpr, WhereClauseOptions};
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::token::Comma;
|
||||
|
||||
use crate::serialization::SerializationDataDef;
|
||||
use crate::{
|
||||
utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME, TYPE_NAME_ATTRIBUTE_NAME,
|
||||
TYPE_PATH_ATTRIBUTE_NAME,
|
||||
|
@ -65,7 +65,7 @@ pub(crate) struct ReflectMeta<'a> {
|
|||
/// ```
|
||||
pub(crate) struct ReflectStruct<'a> {
|
||||
meta: ReflectMeta<'a>,
|
||||
serialization_denylist: BitSet<u32>,
|
||||
serialization_data: Option<SerializationDataDef>,
|
||||
fields: Vec<StructField<'a>>,
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,14 @@ pub(crate) struct StructField<'a> {
|
|||
/// The reflection-based attributes on the field.
|
||||
pub attrs: ReflectFieldAttr,
|
||||
/// The index of this field within the struct.
|
||||
pub index: usize,
|
||||
pub declaration_index: usize,
|
||||
/// The index of this field as seen by the reflection API.
|
||||
///
|
||||
/// This index accounts for the removal of [ignored] fields.
|
||||
/// It will only be `Some(index)` when the field is not ignored.
|
||||
///
|
||||
/// [ignored]: crate::field_attributes::ReflectIgnoreBehavior::IgnoreAlways
|
||||
pub reflection_index: Option<usize>,
|
||||
/// The documentation for this field, if any
|
||||
#[cfg(feature = "documentation")]
|
||||
pub doc: crate::documentation::Documentation,
|
||||
|
@ -272,9 +279,7 @@ impl<'a> ReflectDerive<'a> {
|
|||
let fields = Self::collect_struct_fields(&data.fields)?;
|
||||
let reflect_struct = ReflectStruct {
|
||||
meta,
|
||||
serialization_denylist: members_to_serialization_denylist(
|
||||
fields.iter().map(|v| v.attrs.ignore),
|
||||
),
|
||||
serialization_data: SerializationDataDef::new(&fields)?,
|
||||
fields,
|
||||
};
|
||||
|
||||
|
@ -308,19 +313,31 @@ impl<'a> ReflectDerive<'a> {
|
|||
}
|
||||
|
||||
fn collect_struct_fields(fields: &'a Fields) -> Result<Vec<StructField<'a>>, syn::Error> {
|
||||
let mut active_index = 0;
|
||||
let sifter: utility::ResultSifter<StructField<'a>> = fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, field)| -> Result<StructField, syn::Error> {
|
||||
let attrs = parse_field_attrs(&field.attrs)?;
|
||||
Ok(StructField {
|
||||
index,
|
||||
attrs,
|
||||
data: field,
|
||||
#[cfg(feature = "documentation")]
|
||||
doc: crate::documentation::Documentation::from_attributes(&field.attrs),
|
||||
})
|
||||
})
|
||||
.map(
|
||||
|(declaration_index, field)| -> Result<StructField, syn::Error> {
|
||||
let attrs = parse_field_attrs(&field.attrs)?;
|
||||
|
||||
let reflection_index = if attrs.ignore.is_ignored() {
|
||||
None
|
||||
} else {
|
||||
active_index += 1;
|
||||
Some(active_index - 1)
|
||||
};
|
||||
|
||||
Ok(StructField {
|
||||
declaration_index,
|
||||
reflection_index,
|
||||
attrs,
|
||||
data: field,
|
||||
#[cfg(feature = "documentation")]
|
||||
doc: crate::documentation::Documentation::from_attributes(&field.attrs),
|
||||
})
|
||||
},
|
||||
)
|
||||
.fold(
|
||||
utility::ResultSifter::default(),
|
||||
utility::ResultSifter::fold,
|
||||
|
@ -420,12 +437,9 @@ impl<'a> ReflectStruct<'a> {
|
|||
&self.meta
|
||||
}
|
||||
|
||||
/// Access the data about which fields should be ignored during serialization.
|
||||
///
|
||||
/// The returned bitset is a collection of indices obtained from the [`members_to_serialization_denylist`] function.
|
||||
#[allow(dead_code)]
|
||||
pub fn serialization_denylist(&self) -> &BitSet<u32> {
|
||||
&self.serialization_denylist
|
||||
/// Returns the [`SerializationDataDef`] for this struct.
|
||||
pub fn serialization_data(&self) -> Option<&SerializationDataDef> {
|
||||
self.serialization_data.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
|
||||
|
@ -438,7 +452,7 @@ impl<'a> ReflectStruct<'a> {
|
|||
crate::registration::impl_get_type_registration(
|
||||
self.meta(),
|
||||
where_clause_options,
|
||||
Some(&self.serialization_denylist),
|
||||
self.serialization_data(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -189,7 +189,7 @@ fn get_ignored_fields(reflect_struct: &ReflectStruct) -> MemberValuePair {
|
|||
reflect_struct
|
||||
.ignored_fields()
|
||||
.map(|field| {
|
||||
let member = ident_or_index(field.data.ident.as_ref(), field.index);
|
||||
let member = ident_or_index(field.data.ident.as_ref(), field.declaration_index);
|
||||
|
||||
let value = match &field.attrs.default {
|
||||
DefaultBehavior::Func(path) => quote! {#path()},
|
||||
|
@ -218,8 +218,12 @@ fn get_active_fields(
|
|||
reflect_struct
|
||||
.active_fields()
|
||||
.map(|field| {
|
||||
let member = ident_or_index(field.data.ident.as_ref(), field.index);
|
||||
let accessor = get_field_accessor(field.data, field.index, is_tuple);
|
||||
let member = ident_or_index(field.data.ident.as_ref(), field.declaration_index);
|
||||
let accessor = get_field_accessor(
|
||||
field.data,
|
||||
field.reflection_index.expect("field should be active"),
|
||||
is_tuple,
|
||||
);
|
||||
let ty = field.data.ty.clone();
|
||||
|
||||
let get_field = quote! {
|
||||
|
|
|
@ -346,7 +346,11 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden
|
|||
// Ignored field
|
||||
continue;
|
||||
}
|
||||
constructor_argument.push(generate_for_field(reflect_idx, field.index, field));
|
||||
constructor_argument.push(generate_for_field(
|
||||
reflect_idx,
|
||||
field.declaration_index,
|
||||
field,
|
||||
));
|
||||
reflect_idx += 1;
|
||||
}
|
||||
constructor_argument
|
||||
|
|
|
@ -19,12 +19,12 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS
|
|||
.ident
|
||||
.as_ref()
|
||||
.map(|i| i.to_string())
|
||||
.unwrap_or_else(|| field.index.to_string())
|
||||
.unwrap_or_else(|| field.declaration_index.to_string())
|
||||
})
|
||||
.collect::<Vec<String>>();
|
||||
let field_idents = reflect_struct
|
||||
.active_fields()
|
||||
.map(|field| ident_or_index(field.data.ident.as_ref(), field.index))
|
||||
.map(|field| ident_or_index(field.data.ident.as_ref(), field.declaration_index))
|
||||
.collect::<Vec<_>>();
|
||||
let field_types = reflect_struct.active_types();
|
||||
let field_count = field_idents.len();
|
||||
|
|
|
@ -14,7 +14,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2::
|
|||
|
||||
let field_idents = reflect_struct
|
||||
.active_fields()
|
||||
.map(|field| Member::Unnamed(Index::from(field.index)))
|
||||
.map(|field| Member::Unnamed(Index::from(field.declaration_index)))
|
||||
.collect::<Vec<_>>();
|
||||
let field_types = reflect_struct.active_types();
|
||||
let field_count = field_idents.len();
|
||||
|
|
|
@ -24,6 +24,7 @@ mod from_reflect;
|
|||
mod impls;
|
||||
mod reflect_value;
|
||||
mod registration;
|
||||
mod serialization;
|
||||
mod trait_reflection;
|
||||
mod type_path;
|
||||
mod type_uuid;
|
||||
|
@ -201,8 +202,10 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream {
|
|||
};
|
||||
|
||||
TokenStream::from(quote! {
|
||||
#reflect_impls
|
||||
#from_reflect_impl
|
||||
const _: () = {
|
||||
#reflect_impls
|
||||
#from_reflect_impl
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -241,15 +244,20 @@ pub fn derive_from_reflect(input: TokenStream) -> TokenStream {
|
|||
Err(err) => return err.into_compile_error().into(),
|
||||
};
|
||||
|
||||
match derive_data {
|
||||
let from_reflect_impl = match derive_data {
|
||||
ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => {
|
||||
from_reflect::impl_struct(&struct_data)
|
||||
}
|
||||
ReflectDerive::TupleStruct(struct_data) => from_reflect::impl_tuple_struct(&struct_data),
|
||||
ReflectDerive::Enum(meta) => from_reflect::impl_enum(&meta),
|
||||
ReflectDerive::Value(meta) => from_reflect::impl_value(&meta),
|
||||
}
|
||||
.into()
|
||||
};
|
||||
|
||||
TokenStream::from(quote! {
|
||||
const _: () = {
|
||||
#from_reflect_impl
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
/// Derives the `TypePath` trait, providing a stable alternative to [`std::any::type_name`].
|
||||
|
@ -275,21 +283,31 @@ pub fn derive_type_path(input: TokenStream) -> TokenStream {
|
|||
Err(err) => return err.into_compile_error().into(),
|
||||
};
|
||||
|
||||
impls::impl_type_path(
|
||||
let type_path_impl = impls::impl_type_path(
|
||||
derive_data.meta(),
|
||||
// Use `WhereClauseOptions::new_value` here so we don't enforce reflection bounds
|
||||
&WhereClauseOptions::new_value(derive_data.meta()),
|
||||
)
|
||||
.into()
|
||||
);
|
||||
|
||||
TokenStream::from(quote! {
|
||||
const _: () = {
|
||||
#type_path_impl
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
// From https://github.com/randomPoison/type-uuid
|
||||
#[proc_macro_derive(TypeUuid, attributes(uuid))]
|
||||
pub fn derive_type_uuid(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
type_uuid::type_uuid_derive(input)
|
||||
.unwrap_or_else(syn::Error::into_compile_error)
|
||||
.into()
|
||||
let uuid_impl =
|
||||
type_uuid::type_uuid_derive(input).unwrap_or_else(syn::Error::into_compile_error);
|
||||
|
||||
TokenStream::from(quote! {
|
||||
const _: () = {
|
||||
#uuid_impl
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
/// A macro that automatically generates type data for traits, which their implementors can then register.
|
||||
|
@ -401,8 +419,10 @@ pub fn impl_reflect_value(input: TokenStream) -> TokenStream {
|
|||
let from_reflect_impl = from_reflect::impl_value(&meta);
|
||||
|
||||
TokenStream::from(quote! {
|
||||
#reflect_impls
|
||||
#from_reflect_impl
|
||||
const _: () = {
|
||||
#reflect_impls
|
||||
#from_reflect_impl
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -446,7 +466,7 @@ pub fn impl_reflect_struct(input: TokenStream) -> TokenStream {
|
|||
Err(err) => return err.into_compile_error().into(),
|
||||
};
|
||||
|
||||
match derive_data {
|
||||
let output = match derive_data {
|
||||
ReflectDerive::Struct(struct_data) => {
|
||||
if !struct_data.meta().type_path().has_custom_path() {
|
||||
return syn::Error::new(
|
||||
|
@ -460,27 +480,30 @@ pub fn impl_reflect_struct(input: TokenStream) -> TokenStream {
|
|||
let impl_struct = impls::impl_struct(&struct_data);
|
||||
let impl_from_struct = from_reflect::impl_struct(&struct_data);
|
||||
|
||||
TokenStream::from(quote! {
|
||||
quote! {
|
||||
#impl_struct
|
||||
#impl_from_struct
|
||||
})
|
||||
}
|
||||
}
|
||||
ReflectDerive::TupleStruct(..) => syn::Error::new(
|
||||
ast.span(),
|
||||
"impl_reflect_struct does not support tuple structs",
|
||||
)
|
||||
.into_compile_error()
|
||||
.into(),
|
||||
.into_compile_error(),
|
||||
ReflectDerive::UnitStruct(..) => syn::Error::new(
|
||||
ast.span(),
|
||||
"impl_reflect_struct does not support unit structs",
|
||||
)
|
||||
.into_compile_error()
|
||||
.into(),
|
||||
.into_compile_error(),
|
||||
_ => syn::Error::new(ast.span(), "impl_reflect_struct only supports structs")
|
||||
.into_compile_error()
|
||||
.into(),
|
||||
}
|
||||
.into_compile_error(),
|
||||
};
|
||||
|
||||
TokenStream::from(quote! {
|
||||
const _: () = {
|
||||
#output
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
/// A macro used to generate a `FromReflect` trait implementation for the given type.
|
||||
|
@ -521,7 +544,14 @@ pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream {
|
|||
}
|
||||
};
|
||||
|
||||
from_reflect::impl_value(&ReflectMeta::new(type_path, def.traits.unwrap_or_default())).into()
|
||||
let from_reflect_impl =
|
||||
from_reflect::impl_value(&ReflectMeta::new(type_path, def.traits.unwrap_or_default()));
|
||||
|
||||
TokenStream::from(quote! {
|
||||
const _: () = {
|
||||
#from_reflect_impl
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
/// A replacement for [deriving `TypePath`] for use on foreign types.
|
||||
|
@ -583,12 +613,24 @@ pub fn impl_type_path(input: TokenStream) -> TokenStream {
|
|||
|
||||
let meta = ReflectMeta::new(type_path, ReflectTraits::default());
|
||||
|
||||
impls::impl_type_path(&meta, &WhereClauseOptions::new_value(&meta)).into()
|
||||
let type_path_impl = impls::impl_type_path(&meta, &WhereClauseOptions::new_value(&meta));
|
||||
|
||||
TokenStream::from(quote! {
|
||||
const _: () = {
|
||||
#type_path_impl
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
/// Derives `TypeUuid` for the given type. This is used internally to implement `TypeUuid` on foreign types, such as those in the std. This macro should be used in the format of `<[Generic Params]> [Type (Path)], [Uuid (String Literal)]`.
|
||||
#[proc_macro]
|
||||
pub fn impl_type_uuid(input: TokenStream) -> TokenStream {
|
||||
let def = parse_macro_input!(input as type_uuid::TypeUuidDef);
|
||||
type_uuid::gen_impl_type_uuid(def).into()
|
||||
let uuid_impl = type_uuid::gen_impl_type_uuid(def);
|
||||
|
||||
TokenStream::from(quote! {
|
||||
const _: () = {
|
||||
#uuid_impl
|
||||
};
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
//! Contains code related specifically to Bevy's type registration.
|
||||
|
||||
use crate::derive_data::ReflectMeta;
|
||||
use crate::serialization::SerializationDataDef;
|
||||
use crate::utility::{extend_where_clause, WhereClauseOptions};
|
||||
use bit_set::BitSet;
|
||||
use quote::quote;
|
||||
|
||||
/// Creates the `GetTypeRegistration` impl for the given type data.
|
||||
|
@ -10,7 +10,7 @@ use quote::quote;
|
|||
pub(crate) fn impl_get_type_registration(
|
||||
meta: &ReflectMeta,
|
||||
where_clause_options: &WhereClauseOptions,
|
||||
serialization_denylist: Option<&BitSet<u32>>,
|
||||
serialization_data: Option<&SerializationDataDef>,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let type_path = meta.type_path();
|
||||
let bevy_reflect_path = meta.bevy_reflect_path();
|
||||
|
@ -20,17 +20,16 @@ pub(crate) fn impl_get_type_registration(
|
|||
|
||||
let from_reflect_data = if meta.from_reflect().should_auto_derive() {
|
||||
Some(quote! {
|
||||
registration.insert::<#bevy_reflect_path::ReflectFromReflect>(#bevy_reflect_path::FromType::<Self>::from_type());
|
||||
registration.insert::<#bevy_reflect_path::ReflectFromReflect>(#bevy_reflect_path::FromType::<Self>::from_type());
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let serialization_data = serialization_denylist.map(|denylist| {
|
||||
let denylist = denylist.into_iter();
|
||||
let serialization_data = serialization_data.map(|data| {
|
||||
let serialization_data = data.as_serialization_data(bevy_reflect_path);
|
||||
quote! {
|
||||
let ignored_indices = ::core::iter::IntoIterator::into_iter([#(#denylist),*]);
|
||||
registration.insert::<#bevy_reflect_path::serde::SerializationData>(#bevy_reflect_path::serde::SerializationData::new(ignored_indices));
|
||||
registration.insert::<#bevy_reflect_path::serde::SerializationData>(#serialization_data);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
91
crates/bevy_reflect/bevy_reflect_derive/src/serialization.rs
Normal file
91
crates/bevy_reflect/bevy_reflect_derive/src/serialization.rs
Normal file
|
@ -0,0 +1,91 @@
|
|||
use crate::derive_data::StructField;
|
||||
use crate::field_attributes::{DefaultBehavior, ReflectIgnoreBehavior};
|
||||
use bevy_macro_utils::fq_std::{FQBox, FQDefault};
|
||||
use quote::quote;
|
||||
use std::collections::HashMap;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::Path;
|
||||
|
||||
type ReflectionIndex = usize;
|
||||
|
||||
/// Collected serialization data used to generate a `SerializationData` type.
|
||||
pub(crate) struct SerializationDataDef {
|
||||
/// Maps a field's _reflection_ index to its [`SkippedFieldDef`] if marked as `#[reflect(skip_serializing)]`.
|
||||
skipped: HashMap<ReflectionIndex, SkippedFieldDef>,
|
||||
}
|
||||
|
||||
impl SerializationDataDef {
|
||||
/// Attempts to create a new `SerializationDataDef` from the given collection of fields.
|
||||
///
|
||||
/// Returns `Ok(Some(data))` if there are any fields needing to be skipped during serialization.
|
||||
/// Otherwise, returns `Ok(None)`.
|
||||
pub fn new(fields: &[StructField<'_>]) -> Result<Option<Self>, syn::Error> {
|
||||
let mut skipped = HashMap::default();
|
||||
|
||||
for field in fields {
|
||||
match field.attrs.ignore {
|
||||
ReflectIgnoreBehavior::IgnoreSerialization => {
|
||||
skipped.insert(
|
||||
field.reflection_index.ok_or_else(|| {
|
||||
syn::Error::new(
|
||||
field.data.span(),
|
||||
"internal error: field is missing a reflection index",
|
||||
)
|
||||
})?,
|
||||
SkippedFieldDef::new(field)?,
|
||||
);
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
if skipped.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(Self { skipped }))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a `TokenStream` containing an initialized `SerializationData` type.
|
||||
pub fn as_serialization_data(&self, bevy_reflect_path: &Path) -> proc_macro2::TokenStream {
|
||||
let fields =
|
||||
self.skipped
|
||||
.iter()
|
||||
.map(|(reflection_index, SkippedFieldDef { default_fn })| {
|
||||
quote! {(
|
||||
#reflection_index,
|
||||
#bevy_reflect_path::serde::SkippedField::new(#default_fn)
|
||||
)}
|
||||
});
|
||||
quote! {
|
||||
#bevy_reflect_path::serde::SerializationData::new(
|
||||
::core::iter::IntoIterator::into_iter([#(#fields),*])
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Collected field data used to generate a `SkippedField` type.
|
||||
pub(crate) struct SkippedFieldDef {
|
||||
/// The default function for this field.
|
||||
///
|
||||
/// This is of type `fn() -> Box<dyn Reflect>`.
|
||||
default_fn: proc_macro2::TokenStream,
|
||||
}
|
||||
|
||||
impl SkippedFieldDef {
|
||||
pub fn new(field: &StructField<'_>) -> Result<Self, syn::Error> {
|
||||
let ty = &field.data.ty;
|
||||
|
||||
let default_fn = match &field.attrs.default {
|
||||
DefaultBehavior::Func(func) => quote! {
|
||||
|| { #FQBox::new(#func()) }
|
||||
},
|
||||
_ => quote! {
|
||||
|| { #FQBox::new(<#ty as #FQDefault>::default()) }
|
||||
},
|
||||
};
|
||||
|
||||
Ok(Self { default_fn })
|
||||
}
|
||||
}
|
|
@ -1,12 +1,10 @@
|
|||
//! General-purpose utility functions for internal usage within this crate.
|
||||
|
||||
use crate::derive_data::{ReflectMeta, StructField};
|
||||
use crate::field_attributes::ReflectIgnoreBehavior;
|
||||
use bevy_macro_utils::{
|
||||
fq_std::{FQAny, FQOption, FQSend, FQSync},
|
||||
BevyManifest,
|
||||
};
|
||||
use bit_set::BitSet;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::{spanned::Spanned, LitStr, Member, Path, Type, WhereClause};
|
||||
|
@ -286,42 +284,6 @@ impl<T> ResultSifter<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Converts an iterator over ignore behavior of members to a bitset of ignored members.
|
||||
///
|
||||
/// Takes into account the fact that always ignored (non-reflected) members are skipped.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust,ignore
|
||||
/// pub struct HelloWorld {
|
||||
/// reflected_field: u32 // index: 0
|
||||
///
|
||||
/// #[reflect(ignore)]
|
||||
/// non_reflected_field: u32 // index: N/A (not 1!)
|
||||
///
|
||||
/// #[reflect(skip_serializing)]
|
||||
/// non_serialized_field: u32 // index: 1
|
||||
/// }
|
||||
/// ```
|
||||
/// Would convert to the `0b01` bitset (i.e second field is NOT serialized)
|
||||
///
|
||||
pub(crate) fn members_to_serialization_denylist<T>(member_iter: T) -> BitSet<u32>
|
||||
where
|
||||
T: Iterator<Item = ReflectIgnoreBehavior>,
|
||||
{
|
||||
let mut bitset = BitSet::default();
|
||||
|
||||
member_iter.fold(0, |next_idx, member| match member {
|
||||
ReflectIgnoreBehavior::IgnoreAlways => next_idx,
|
||||
ReflectIgnoreBehavior::IgnoreSerialization => {
|
||||
bitset.insert(next_idx);
|
||||
next_idx + 1
|
||||
}
|
||||
ReflectIgnoreBehavior::None => next_idx + 1,
|
||||
});
|
||||
|
||||
bitset
|
||||
}
|
||||
|
||||
/// Turns an `Option<TokenStream>` into a `TokenStream` for an `Option`.
|
||||
pub(crate) fn wrap_in_option(tokens: Option<proc_macro2::TokenStream>) -> proc_macro2::TokenStream {
|
||||
match tokens {
|
||||
|
|
|
@ -764,6 +764,39 @@ mod tests {
|
|||
.unwrap_or_default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_reflect_should_allow_ignored_unnamed_fields() {
|
||||
#[derive(Reflect, Eq, PartialEq, Debug)]
|
||||
struct MyTupleStruct(i8, #[reflect(ignore)] i16, i32);
|
||||
|
||||
let expected = MyTupleStruct(1, 0, 3);
|
||||
|
||||
let mut dyn_tuple_struct = DynamicTupleStruct::default();
|
||||
dyn_tuple_struct.insert(1_i8);
|
||||
dyn_tuple_struct.insert(3_i32);
|
||||
let my_tuple_struct = <MyTupleStruct as FromReflect>::from_reflect(&dyn_tuple_struct);
|
||||
|
||||
assert_eq!(Some(expected), my_tuple_struct);
|
||||
|
||||
#[derive(Reflect, Eq, PartialEq, Debug)]
|
||||
enum MyEnum {
|
||||
Tuple(i8, #[reflect(ignore)] i16, i32),
|
||||
}
|
||||
|
||||
let expected = MyEnum::Tuple(1, 0, 3);
|
||||
|
||||
let mut dyn_tuple = DynamicTuple::default();
|
||||
dyn_tuple.insert(1_i8);
|
||||
dyn_tuple.insert(3_i32);
|
||||
|
||||
let mut dyn_enum = DynamicEnum::default();
|
||||
dyn_enum.set_variant("Tuple", dyn_tuple);
|
||||
|
||||
let my_enum = <MyEnum as FromReflect>::from_reflect(&dyn_enum);
|
||||
|
||||
assert_eq!(Some(expected), my_enum);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_reflect_should_use_default_field_attributes() {
|
||||
#[derive(Reflect, Eq, PartialEq, Debug)]
|
||||
|
|
|
@ -2,9 +2,8 @@ use crate::serde::SerializationData;
|
|||
use crate::{
|
||||
ArrayInfo, DynamicArray, DynamicEnum, DynamicList, DynamicMap, DynamicStruct, DynamicTuple,
|
||||
DynamicTupleStruct, DynamicVariant, EnumInfo, ListInfo, Map, MapInfo, NamedField, Reflect,
|
||||
ReflectDeserialize, StructInfo, StructVariantInfo, Tuple, TupleInfo, TupleStruct,
|
||||
TupleStructInfo, TupleVariantInfo, TypeInfo, TypeRegistration, TypeRegistry, UnnamedField,
|
||||
VariantInfo,
|
||||
ReflectDeserialize, StructInfo, StructVariantInfo, TupleInfo, TupleStructInfo,
|
||||
TupleVariantInfo, TypeInfo, TypeRegistration, TypeRegistry, UnnamedField, VariantInfo,
|
||||
};
|
||||
use erased_serde::Deserializer;
|
||||
use serde::de::{
|
||||
|
@ -27,6 +26,8 @@ pub trait DeserializeValue {
|
|||
trait StructLikeInfo {
|
||||
fn get_path(&self) -> &str;
|
||||
fn get_field(&self, name: &str) -> Option<&NamedField>;
|
||||
fn field_at(&self, index: usize) -> Option<&NamedField>;
|
||||
fn get_field_len(&self) -> usize;
|
||||
fn iter_fields(&self) -> Iter<'_, NamedField>;
|
||||
}
|
||||
|
||||
|
@ -49,10 +50,18 @@ impl StructLikeInfo for StructInfo {
|
|||
self.type_path()
|
||||
}
|
||||
|
||||
fn field_at(&self, index: usize) -> Option<&NamedField> {
|
||||
self.field_at(index)
|
||||
}
|
||||
|
||||
fn get_field(&self, name: &str) -> Option<&NamedField> {
|
||||
self.field(name)
|
||||
}
|
||||
|
||||
fn get_field_len(&self) -> usize {
|
||||
self.field_len()
|
||||
}
|
||||
|
||||
fn iter_fields(&self) -> Iter<'_, NamedField> {
|
||||
self.iter()
|
||||
}
|
||||
|
@ -80,10 +89,18 @@ impl StructLikeInfo for StructVariantInfo {
|
|||
self.name()
|
||||
}
|
||||
|
||||
fn field_at(&self, index: usize) -> Option<&NamedField> {
|
||||
self.field_at(index)
|
||||
}
|
||||
|
||||
fn get_field(&self, name: &str) -> Option<&NamedField> {
|
||||
self.field(name)
|
||||
}
|
||||
|
||||
fn get_field_len(&self) -> usize {
|
||||
self.field_len()
|
||||
}
|
||||
|
||||
fn iter_fields(&self) -> Iter<'_, NamedField> {
|
||||
self.iter()
|
||||
}
|
||||
|
@ -120,6 +137,54 @@ impl TupleLikeInfo for TupleInfo {
|
|||
}
|
||||
}
|
||||
|
||||
impl Container for TupleInfo {
|
||||
fn get_field_registration<'a, E: Error>(
|
||||
&self,
|
||||
index: usize,
|
||||
registry: &'a TypeRegistry,
|
||||
) -> Result<&'a TypeRegistration, E> {
|
||||
let field = self.field_at(index).ok_or_else(|| {
|
||||
de::Error::custom(format_args!(
|
||||
"no field at index {} on tuple {}",
|
||||
index,
|
||||
self.type_path(),
|
||||
))
|
||||
})?;
|
||||
get_registration(field.type_id(), field.type_path(), registry)
|
||||
}
|
||||
}
|
||||
|
||||
impl TupleLikeInfo for TupleStructInfo {
|
||||
fn get_path(&self) -> &str {
|
||||
self.type_path()
|
||||
}
|
||||
|
||||
fn get_field(&self, index: usize) -> Option<&UnnamedField> {
|
||||
self.field_at(index)
|
||||
}
|
||||
|
||||
fn get_field_len(&self) -> usize {
|
||||
self.field_len()
|
||||
}
|
||||
}
|
||||
|
||||
impl Container for TupleStructInfo {
|
||||
fn get_field_registration<'a, E: Error>(
|
||||
&self,
|
||||
index: usize,
|
||||
registry: &'a TypeRegistry,
|
||||
) -> Result<&'a TypeRegistration, E> {
|
||||
let field = self.field_at(index).ok_or_else(|| {
|
||||
de::Error::custom(format_args!(
|
||||
"no field at index {} on tuple struct {}",
|
||||
index,
|
||||
self.type_path(),
|
||||
))
|
||||
})?;
|
||||
get_registration(field.type_id(), field.type_path(), registry)
|
||||
}
|
||||
}
|
||||
|
||||
impl TupleLikeInfo for TupleVariantInfo {
|
||||
fn get_path(&self) -> &str {
|
||||
self.name()
|
||||
|
@ -134,6 +199,23 @@ impl TupleLikeInfo for TupleVariantInfo {
|
|||
}
|
||||
}
|
||||
|
||||
impl Container for TupleVariantInfo {
|
||||
fn get_field_registration<'a, E: Error>(
|
||||
&self,
|
||||
index: usize,
|
||||
registry: &'a TypeRegistry,
|
||||
) -> Result<&'a TypeRegistration, E> {
|
||||
let field = self.field_at(index).ok_or_else(|| {
|
||||
de::Error::custom(format_args!(
|
||||
"no field at index {} on tuple variant {}",
|
||||
index,
|
||||
self.name(),
|
||||
))
|
||||
})?;
|
||||
get_registration(field.type_id(), field.type_path(), registry)
|
||||
}
|
||||
}
|
||||
|
||||
/// A debug struct used for error messages that displays a list of expected values.
|
||||
///
|
||||
/// # Example
|
||||
|
@ -444,6 +526,7 @@ impl<'a, 'de> DeserializeSeed<'de> for TypedReflectDeserializer<'a> {
|
|||
tuple_info.field_len(),
|
||||
TupleVisitor {
|
||||
tuple_info,
|
||||
registration: self.registration,
|
||||
registry: self.registry,
|
||||
},
|
||||
)?;
|
||||
|
@ -500,43 +583,14 @@ impl<'a, 'de> Visitor<'de> for StructVisitor<'a> {
|
|||
where
|
||||
V: MapAccess<'de>,
|
||||
{
|
||||
visit_struct(&mut map, self.struct_info, self.registry)
|
||||
visit_struct(&mut map, self.struct_info, self.registration, self.registry)
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: SeqAccess<'de>,
|
||||
{
|
||||
let mut index = 0usize;
|
||||
let mut output = DynamicStruct::default();
|
||||
|
||||
let ignored_len = self
|
||||
.registration
|
||||
.data::<SerializationData>()
|
||||
.map(|data| data.len())
|
||||
.unwrap_or(0);
|
||||
let field_len = self.struct_info.field_len().saturating_sub(ignored_len);
|
||||
|
||||
if field_len == 0 {
|
||||
// Handle unit structs and ignored fields
|
||||
return Ok(output);
|
||||
}
|
||||
|
||||
while let Some(value) = seq.next_element_seed(TypedReflectDeserializer {
|
||||
registration: self
|
||||
.struct_info
|
||||
.get_field_registration(index, self.registry)?,
|
||||
registry: self.registry,
|
||||
})? {
|
||||
let name = self.struct_info.field_at(index).unwrap().name();
|
||||
output.insert_boxed(name, value);
|
||||
index += 1;
|
||||
if index >= self.struct_info.field_len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(output)
|
||||
visit_struct_seq(&mut seq, self.struct_info, self.registration, self.registry)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -557,64 +611,19 @@ impl<'a, 'de> Visitor<'de> for TupleStructVisitor<'a> {
|
|||
where
|
||||
V: SeqAccess<'de>,
|
||||
{
|
||||
let mut index = 0usize;
|
||||
let mut tuple_struct = DynamicTupleStruct::default();
|
||||
|
||||
let ignored_len = self
|
||||
.registration
|
||||
.data::<SerializationData>()
|
||||
.map(|data| data.len())
|
||||
.unwrap_or(0);
|
||||
let field_len = self
|
||||
.tuple_struct_info
|
||||
.field_len()
|
||||
.saturating_sub(ignored_len);
|
||||
|
||||
if field_len == 0 {
|
||||
// Handle unit structs and ignored fields
|
||||
return Ok(tuple_struct);
|
||||
}
|
||||
|
||||
let get_field_registration = |index: usize| -> Result<&'a TypeRegistration, V::Error> {
|
||||
let field = self.tuple_struct_info.field_at(index).ok_or_else(|| {
|
||||
de::Error::custom(format_args!(
|
||||
"no field at index {} on tuple {}",
|
||||
index,
|
||||
self.tuple_struct_info.type_path(),
|
||||
))
|
||||
})?;
|
||||
get_registration(field.type_id(), field.type_path(), self.registry)
|
||||
};
|
||||
|
||||
while let Some(value) = seq.next_element_seed(TypedReflectDeserializer {
|
||||
registration: get_field_registration(index)?,
|
||||
registry: self.registry,
|
||||
})? {
|
||||
tuple_struct.insert_boxed(value);
|
||||
index += 1;
|
||||
if index >= self.tuple_struct_info.field_len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let ignored_len = self
|
||||
.registration
|
||||
.data::<SerializationData>()
|
||||
.map(|data| data.len())
|
||||
.unwrap_or(0);
|
||||
if tuple_struct.field_len() != self.tuple_struct_info.field_len() - ignored_len {
|
||||
return Err(Error::invalid_length(
|
||||
tuple_struct.field_len(),
|
||||
&self.tuple_struct_info.field_len().to_string().as_str(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(tuple_struct)
|
||||
visit_tuple(
|
||||
&mut seq,
|
||||
self.tuple_struct_info,
|
||||
self.registration,
|
||||
self.registry,
|
||||
)
|
||||
.map(DynamicTupleStruct::from)
|
||||
}
|
||||
}
|
||||
|
||||
struct TupleVisitor<'a> {
|
||||
tuple_info: &'static TupleInfo,
|
||||
registration: &'a TypeRegistration,
|
||||
registry: &'a TypeRegistry,
|
||||
}
|
||||
|
||||
|
@ -629,7 +638,7 @@ impl<'a, 'de> Visitor<'de> for TupleVisitor<'a> {
|
|||
where
|
||||
V: SeqAccess<'de>,
|
||||
{
|
||||
visit_tuple(&mut seq, self.tuple_info, self.registry)
|
||||
visit_tuple(&mut seq, self.tuple_info, self.registration, self.registry)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -782,9 +791,7 @@ impl<'a, 'de> Visitor<'de> for EnumVisitor<'a> {
|
|||
)?
|
||||
.into(),
|
||||
VariantInfo::Tuple(tuple_info) if tuple_info.field_len() == 1 => {
|
||||
let field = tuple_info.field_at(0).unwrap();
|
||||
let registration =
|
||||
get_registration(field.type_id(), field.type_path(), self.registry)?;
|
||||
let registration = tuple_info.get_field_registration(0, self.registry)?;
|
||||
let value = variant.newtype_variant_seed(TypedReflectDeserializer {
|
||||
registration,
|
||||
registry: self.registry,
|
||||
|
@ -879,43 +886,14 @@ impl<'a, 'de> Visitor<'de> for StructVariantVisitor<'a> {
|
|||
where
|
||||
V: MapAccess<'de>,
|
||||
{
|
||||
visit_struct(&mut map, self.struct_info, self.registry)
|
||||
visit_struct(&mut map, self.struct_info, self.registration, self.registry)
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: SeqAccess<'de>,
|
||||
{
|
||||
let mut index = 0usize;
|
||||
let mut output = DynamicStruct::default();
|
||||
|
||||
let ignored_len = self
|
||||
.registration
|
||||
.data::<SerializationData>()
|
||||
.map(|data| data.len())
|
||||
.unwrap_or(0);
|
||||
let field_len = self.struct_info.field_len().saturating_sub(ignored_len);
|
||||
|
||||
if field_len == 0 {
|
||||
// Handle all fields being ignored
|
||||
return Ok(output);
|
||||
}
|
||||
|
||||
while let Some(value) = seq.next_element_seed(TypedReflectDeserializer {
|
||||
registration: self
|
||||
.struct_info
|
||||
.get_field_registration(index, self.registry)?,
|
||||
registry: self.registry,
|
||||
})? {
|
||||
let name = self.struct_info.field_at(index).unwrap().name();
|
||||
output.insert_boxed(name, value);
|
||||
index += 1;
|
||||
if index >= self.struct_info.field_len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(output)
|
||||
visit_struct_seq(&mut seq, self.struct_info, self.registration, self.registry)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -936,19 +914,7 @@ impl<'a, 'de> Visitor<'de> for TupleVariantVisitor<'a> {
|
|||
where
|
||||
V: SeqAccess<'de>,
|
||||
{
|
||||
let ignored_len = self
|
||||
.registration
|
||||
.data::<SerializationData>()
|
||||
.map(|data| data.len())
|
||||
.unwrap_or(0);
|
||||
let field_len = self.tuple_info.field_len().saturating_sub(ignored_len);
|
||||
|
||||
if field_len == 0 {
|
||||
// Handle all fields being ignored
|
||||
return Ok(DynamicTuple::default());
|
||||
}
|
||||
|
||||
visit_tuple(&mut seq, self.tuple_info, self.registry)
|
||||
visit_tuple(&mut seq, self.tuple_info, self.registration, self.registry)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1005,6 +971,7 @@ impl<'a, 'de> Visitor<'de> for OptionVisitor<'a> {
|
|||
fn visit_struct<'de, T, V>(
|
||||
map: &mut V,
|
||||
info: &'static T,
|
||||
registration: &TypeRegistration,
|
||||
registry: &TypeRegistry,
|
||||
) -> Result<DynamicStruct, V::Error>
|
||||
where
|
||||
|
@ -1029,51 +996,103 @@ where
|
|||
dynamic_struct.insert_boxed(&key, value);
|
||||
}
|
||||
|
||||
if let Some(serialization_data) = registration.data::<SerializationData>() {
|
||||
for (skipped_index, skipped_field) in serialization_data.iter_skipped() {
|
||||
let Some(field) = info.field_at(*skipped_index) else {
|
||||
continue;
|
||||
};
|
||||
dynamic_struct.insert_boxed(field.name(), skipped_field.generate_default());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(dynamic_struct)
|
||||
}
|
||||
|
||||
fn visit_tuple<'de, T, V>(
|
||||
seq: &mut V,
|
||||
info: &T,
|
||||
registration: &TypeRegistration,
|
||||
registry: &TypeRegistry,
|
||||
) -> Result<DynamicTuple, V::Error>
|
||||
where
|
||||
T: TupleLikeInfo,
|
||||
T: TupleLikeInfo + Container,
|
||||
V: SeqAccess<'de>,
|
||||
{
|
||||
let mut tuple = DynamicTuple::default();
|
||||
let mut index = 0usize;
|
||||
|
||||
let get_field_registration = |index: usize| -> Result<&TypeRegistration, V::Error> {
|
||||
let field = info.get_field(index).ok_or_else(|| {
|
||||
Error::invalid_length(index, &info.get_field_len().to_string().as_str())
|
||||
})?;
|
||||
get_registration(field.type_id(), field.type_path(), registry)
|
||||
};
|
||||
|
||||
while let Some(value) = seq.next_element_seed(TypedReflectDeserializer {
|
||||
registration: get_field_registration(index)?,
|
||||
registry,
|
||||
})? {
|
||||
tuple.insert_boxed(value);
|
||||
index += 1;
|
||||
if index >= info.get_field_len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let len = info.get_field_len();
|
||||
|
||||
if tuple.field_len() != len {
|
||||
return Err(Error::invalid_length(
|
||||
tuple.field_len(),
|
||||
&len.to_string().as_str(),
|
||||
));
|
||||
if len == 0 {
|
||||
// Handle empty tuple/tuple struct
|
||||
return Ok(tuple);
|
||||
}
|
||||
|
||||
let serialization_data = registration.data::<SerializationData>();
|
||||
|
||||
for index in 0..len {
|
||||
if let Some(value) = serialization_data.and_then(|data| data.generate_default(index)) {
|
||||
tuple.insert_boxed(value);
|
||||
continue;
|
||||
}
|
||||
|
||||
let value = seq
|
||||
.next_element_seed(TypedReflectDeserializer {
|
||||
registration: info.get_field_registration(index, registry)?,
|
||||
registry,
|
||||
})?
|
||||
.ok_or_else(|| Error::invalid_length(index, &len.to_string().as_str()))?;
|
||||
tuple.insert_boxed(value);
|
||||
}
|
||||
|
||||
Ok(tuple)
|
||||
}
|
||||
|
||||
fn visit_struct_seq<'de, T, V>(
|
||||
seq: &mut V,
|
||||
info: &T,
|
||||
registration: &TypeRegistration,
|
||||
registry: &TypeRegistry,
|
||||
) -> Result<DynamicStruct, V::Error>
|
||||
where
|
||||
T: StructLikeInfo + Container,
|
||||
V: SeqAccess<'de>,
|
||||
{
|
||||
let mut dynamic_struct = DynamicStruct::default();
|
||||
|
||||
let len = info.get_field_len();
|
||||
|
||||
if len == 0 {
|
||||
// Handle unit structs
|
||||
return Ok(dynamic_struct);
|
||||
}
|
||||
|
||||
let serialization_data = registration.data::<SerializationData>();
|
||||
|
||||
for index in 0..len {
|
||||
let name = info.field_at(index).unwrap().name();
|
||||
|
||||
if serialization_data
|
||||
.map(|data| data.is_field_skipped(index))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
if let Some(value) = serialization_data.unwrap().generate_default(index) {
|
||||
dynamic_struct.insert_boxed(name, value);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let value = seq
|
||||
.next_element_seed(TypedReflectDeserializer {
|
||||
registration: info.get_field_registration(index, registry)?,
|
||||
registry,
|
||||
})?
|
||||
.ok_or_else(|| Error::invalid_length(index, &len.to_string().as_str()))?;
|
||||
dynamic_struct.insert_boxed(name, value);
|
||||
}
|
||||
|
||||
Ok(dynamic_struct)
|
||||
}
|
||||
|
||||
fn get_registration<'a, E: Error>(
|
||||
type_id: TypeId,
|
||||
type_path: &str,
|
||||
|
|
|
@ -12,7 +12,7 @@ mod tests {
|
|||
use crate::{
|
||||
serde::{ReflectSerializer, UntypedReflectDeserializer},
|
||||
type_registry::TypeRegistry,
|
||||
DynamicStruct, Reflect,
|
||||
DynamicStruct, FromReflect, Reflect,
|
||||
};
|
||||
use serde::de::DeserializeSeed;
|
||||
|
||||
|
@ -26,7 +26,14 @@ mod tests {
|
|||
b: i32,
|
||||
#[reflect(skip_serializing)]
|
||||
c: i32,
|
||||
#[reflect(skip_serializing)]
|
||||
#[reflect(default = "custom_default")]
|
||||
d: i32,
|
||||
e: i32,
|
||||
}
|
||||
|
||||
fn custom_default() -> i32 {
|
||||
-1
|
||||
}
|
||||
|
||||
let mut registry = TypeRegistry::default();
|
||||
|
@ -37,24 +44,42 @@ mod tests {
|
|||
b: 4,
|
||||
c: 5,
|
||||
d: 6,
|
||||
e: 7,
|
||||
};
|
||||
|
||||
let serializer = ReflectSerializer::new(&test_struct, ®istry);
|
||||
let serialized =
|
||||
ron::ser::to_string_pretty(&serializer, ron::ser::PrettyConfig::default()).unwrap();
|
||||
|
||||
let mut expected = DynamicStruct::default();
|
||||
expected.insert("a", 3);
|
||||
expected.insert("d", 6);
|
||||
|
||||
let mut deserializer = ron::de::Deserializer::from_str(&serialized).unwrap();
|
||||
let reflect_deserializer = UntypedReflectDeserializer::new(®istry);
|
||||
let value = reflect_deserializer.deserialize(&mut deserializer).unwrap();
|
||||
let deserialized = value.take::<DynamicStruct>().unwrap();
|
||||
|
||||
let mut expected = DynamicStruct::default();
|
||||
expected.insert("a", 3);
|
||||
// Ignored: expected.insert("b", 0);
|
||||
expected.insert("c", 0);
|
||||
expected.insert("d", -1);
|
||||
expected.insert("e", 7);
|
||||
|
||||
assert!(
|
||||
expected.reflect_partial_eq(&deserialized).unwrap(),
|
||||
"Expected {expected:?} found {deserialized:?}"
|
||||
"Deserialization failed: expected {expected:?} found {deserialized:?}"
|
||||
);
|
||||
|
||||
let expected = TestStruct {
|
||||
a: 3,
|
||||
b: 0,
|
||||
c: 0,
|
||||
d: -1,
|
||||
e: 7,
|
||||
};
|
||||
let received = <TestStruct as FromReflect>::from_reflect(&deserialized).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
expected, received,
|
||||
"FromReflect failed: expected {expected:?} found {received:?}"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -66,30 +91,48 @@ mod tests {
|
|||
i32,
|
||||
#[reflect(ignore)] i32,
|
||||
#[reflect(skip_serializing)] i32,
|
||||
#[reflect(skip_serializing)]
|
||||
#[reflect(default = "custom_default")]
|
||||
i32,
|
||||
i32,
|
||||
);
|
||||
|
||||
fn custom_default() -> i32 {
|
||||
-1
|
||||
}
|
||||
|
||||
let mut registry = TypeRegistry::default();
|
||||
registry.register::<TestStruct>();
|
||||
|
||||
let test_struct = TestStruct(3, 4, 5, 6);
|
||||
let test_struct = TestStruct(3, 4, 5, 6, 7);
|
||||
|
||||
let serializer = ReflectSerializer::new(&test_struct, ®istry);
|
||||
let serialized =
|
||||
ron::ser::to_string_pretty(&serializer, ron::ser::PrettyConfig::default()).unwrap();
|
||||
|
||||
let mut expected = DynamicTupleStruct::default();
|
||||
expected.insert(3);
|
||||
expected.insert(6);
|
||||
|
||||
let mut deserializer = ron::de::Deserializer::from_str(&serialized).unwrap();
|
||||
let reflect_deserializer = UntypedReflectDeserializer::new(®istry);
|
||||
let value = reflect_deserializer.deserialize(&mut deserializer).unwrap();
|
||||
let deserialized = value.take::<DynamicTupleStruct>().unwrap();
|
||||
|
||||
let mut expected = DynamicTupleStruct::default();
|
||||
expected.insert(3);
|
||||
// Ignored: expected.insert(0);
|
||||
expected.insert(0);
|
||||
expected.insert(-1);
|
||||
expected.insert(7);
|
||||
|
||||
assert!(
|
||||
expected.reflect_partial_eq(&deserialized).unwrap(),
|
||||
"Expected {expected:?} found {deserialized:?}"
|
||||
"Deserialization failed: expected {expected:?} found {deserialized:?}"
|
||||
);
|
||||
|
||||
let expected = TestStruct(3, 0, 0, -1, 7);
|
||||
let received = <TestStruct as FromReflect>::from_reflect(&deserialized).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
expected, received,
|
||||
"FromReflect failed: expected {expected:?} found {received:?}"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -212,7 +212,7 @@ impl<'a> Serialize for StructSerializer<'a> {
|
|||
|
||||
for (index, value) in self.struct_value.iter_fields().enumerate() {
|
||||
if serialization_data
|
||||
.map(|data| data.is_ignored_field(index))
|
||||
.map(|data| data.is_field_skipped(index))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
continue;
|
||||
|
@ -265,7 +265,7 @@ impl<'a> Serialize for TupleStructSerializer<'a> {
|
|||
|
||||
for (index, value) in self.tuple_struct.iter_fields().enumerate() {
|
||||
if serialization_data
|
||||
.map(|data| data.is_ignored_field(index))
|
||||
.map(|data| data.is_field_skipped(index))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
continue;
|
||||
|
|
|
@ -1,44 +1,136 @@
|
|||
use std::collections::HashSet;
|
||||
use crate::Reflect;
|
||||
use bevy_utils::hashbrown::hash_map::Iter;
|
||||
use bevy_utils::HashMap;
|
||||
|
||||
/// Contains data relevant to the automatic reflect powered serialization of a type
|
||||
/// Contains data relevant to the automatic reflect powered (de)serialization of a type.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SerializationData {
|
||||
ignored_field_indices: HashSet<usize>,
|
||||
skipped_fields: HashMap<usize, SkippedField>,
|
||||
}
|
||||
|
||||
impl SerializationData {
|
||||
/// Creates a new `SerializationData` instance given:
|
||||
/// Creates a new `SerializationData` instance with the given skipped fields.
|
||||
///
|
||||
/// - `ignored_iter`: the iterator of member indices to be ignored during serialization. Indices are assigned only to reflected members, those which are not reflected are skipped.
|
||||
pub fn new<I: Iterator<Item = usize>>(ignored_iter: I) -> Self {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `skipped_iter`: The iterator of field indices to be skipped during (de)serialization.
|
||||
/// Indices are assigned only to reflected fields.
|
||||
/// Ignored fields (i.e. those marked `#[reflect(ignore)]`) are implicitly skipped
|
||||
/// and do not need to be included in this iterator.
|
||||
pub fn new<I: Iterator<Item = (usize, SkippedField)>>(skipped_iter: I) -> Self {
|
||||
Self {
|
||||
ignored_field_indices: ignored_iter.collect(),
|
||||
skipped_fields: skipped_iter.collect(),
|
||||
}
|
||||
}
|
||||
/// Returns true if the given index corresponds to a field meant to be ignored in serialization.
|
||||
///
|
||||
/// Indices start from 0 and ignored fields are skipped.
|
||||
/// Returns true if the given index corresponds to a field meant to be skipped during (de)serialization.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// ```
|
||||
/// # use std::any::TypeId;
|
||||
/// # use bevy_reflect::{Reflect, Struct, TypeRegistry, serde::SerializationData};
|
||||
/// #[derive(Reflect)]
|
||||
/// struct MyStruct {
|
||||
/// serialize_me: i32,
|
||||
/// #[reflect(skip_serializing)]
|
||||
/// skip_me: i32
|
||||
/// }
|
||||
///
|
||||
/// let mut registry = TypeRegistry::new();
|
||||
/// registry.register::<MyStruct>();
|
||||
///
|
||||
/// let my_struct = MyStruct {
|
||||
/// serialize_me: 123,
|
||||
/// skip_me: 321,
|
||||
/// };
|
||||
///
|
||||
/// let serialization_data = registry.get_type_data::<SerializationData>(TypeId::of::<MyStruct>()).unwrap();
|
||||
///
|
||||
/// for (idx, field) in my_struct.iter_fields().enumerate(){
|
||||
/// if serialization_data.is_ignored_field(idx){
|
||||
/// // serialize ...
|
||||
/// }
|
||||
/// if serialization_data.is_field_skipped(idx) {
|
||||
/// // Skipped!
|
||||
/// assert_eq!(1, idx);
|
||||
/// } else {
|
||||
/// // Not Skipped!
|
||||
/// assert_eq!(0, idx);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn is_ignored_field(&self, index: usize) -> bool {
|
||||
self.ignored_field_indices.contains(&index)
|
||||
pub fn is_field_skipped(&self, index: usize) -> bool {
|
||||
self.skipped_fields.contains_key(&index)
|
||||
}
|
||||
|
||||
/// Returns the number of ignored fields.
|
||||
/// Generates a default instance of the skipped field at the given index.
|
||||
///
|
||||
/// Returns `None` if the field is not skipped.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use std::any::TypeId;
|
||||
/// # use bevy_reflect::{Reflect, Struct, TypeRegistry, serde::SerializationData};
|
||||
/// #[derive(Reflect)]
|
||||
/// struct MyStruct {
|
||||
/// serialize_me: i32,
|
||||
/// #[reflect(skip_serializing)]
|
||||
/// #[reflect(default = "skip_me_default")]
|
||||
/// skip_me: i32
|
||||
/// }
|
||||
///
|
||||
/// fn skip_me_default() -> i32 {
|
||||
/// 789
|
||||
/// }
|
||||
///
|
||||
/// let mut registry = TypeRegistry::new();
|
||||
/// registry.register::<MyStruct>();
|
||||
///
|
||||
/// let serialization_data = registry.get_type_data::<SerializationData>(TypeId::of::<MyStruct>()).unwrap();
|
||||
/// assert_eq!(789, serialization_data.generate_default(1).unwrap().take::<i32>().unwrap());
|
||||
/// ```
|
||||
pub fn generate_default(&self, index: usize) -> Option<Box<dyn Reflect>> {
|
||||
self.skipped_fields
|
||||
.get(&index)
|
||||
.map(|field| field.generate_default())
|
||||
}
|
||||
|
||||
/// Returns the number of skipped fields.
|
||||
pub fn len(&self) -> usize {
|
||||
self.ignored_field_indices.len()
|
||||
self.skipped_fields.len()
|
||||
}
|
||||
|
||||
/// Returns true if there are no ignored fields.
|
||||
/// Returns true if there are no skipped fields.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.ignored_field_indices.is_empty()
|
||||
self.skipped_fields.is_empty()
|
||||
}
|
||||
|
||||
/// Returns an iterator over the skipped fields.
|
||||
///
|
||||
/// Each item in the iterator is a tuple containing:
|
||||
/// 1. The reflected index of the field
|
||||
/// 2. The (de)serialization metadata of the field
|
||||
pub fn iter_skipped(&self) -> Iter<'_, usize, SkippedField> {
|
||||
self.skipped_fields.iter()
|
||||
}
|
||||
}
|
||||
|
||||
/// Data needed for (de)serialization of a skipped field.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SkippedField {
|
||||
default_fn: fn() -> Box<dyn Reflect>,
|
||||
}
|
||||
|
||||
impl SkippedField {
|
||||
/// Create a new `SkippedField`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `default_fn`: A function pointer used to generate a default instance of the field.
|
||||
pub fn new(default_fn: fn() -> Box<dyn Reflect>) -> Self {
|
||||
Self { default_fn }
|
||||
}
|
||||
|
||||
/// Generates a default instance of the field.
|
||||
pub fn generate_default(&self) -> Box<dyn Reflect> {
|
||||
(self.default_fn)()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use bevy_reflect_derive::impl_type_path;
|
||||
|
||||
use crate::{
|
||||
self as bevy_reflect, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath,
|
||||
TypePathTable, UnnamedField,
|
||||
self as bevy_reflect, DynamicTuple, Reflect, ReflectMut, ReflectOwned, ReflectRef, Tuple,
|
||||
TypeInfo, TypePath, TypePathTable, UnnamedField,
|
||||
};
|
||||
use std::any::{Any, TypeId};
|
||||
use std::fmt::{Debug, Formatter};
|
||||
|
@ -390,6 +390,15 @@ impl Debug for DynamicTupleStruct {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<DynamicTuple> for DynamicTupleStruct {
|
||||
fn from(value: DynamicTuple) -> Self {
|
||||
Self {
|
||||
represented_type: None,
|
||||
fields: Box::new(value).drain(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compares a [`TupleStruct`] with a [`Reflect`] value.
|
||||
///
|
||||
/// Returns true if and only if all of the following are true:
|
||||
|
|
Loading…
Reference in a new issue