Skip to content

Commit

Permalink
add skip_serializing flag to reflect macros
Browse files Browse the repository at this point in the history
  • Loading branch information
makspll committed Sep 8, 2022
1 parent bf1ca81 commit cf9b287
Show file tree
Hide file tree
Showing 11 changed files with 257 additions and 25 deletions.
49 changes: 38 additions & 11 deletions crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::container_attributes::ReflectTraits;
use crate::field_attributes::{parse_field_attrs, ReflectFieldAttr};
use crate::field_attributes::{parse_field_attrs, ReflectFieldAttr, ReflectFieldIgnore};
use crate::utility::get_bevy_reflect_path;
use crate::{REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME};
use syn::{Data, DataStruct, DeriveInput, Field, Fields, Generics, Ident, Meta, Path};
Expand Down Expand Up @@ -137,21 +137,34 @@ impl<'a> ReflectDeriveData<'a> {
Ok(output)
}

/// Get an iterator over the active fields
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
self.fields.iter().filter(|field| !field.attrs.ignore)
/// Get an iterator over the fields satisfying the given predicate
fn fields_with<F: FnMut(&&StructField) -> bool>(
&self,
predicate: F,
) -> impl Iterator<Item = &StructField<'a>> {
self.fields.iter().filter(predicate)
}

/// Get an iterator over the ignored fields
pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
self.fields.iter().filter(|field| field.attrs.ignore)
/// Get a collection of all field types satisfying the given predicate
fn types_with<F: FnMut(&&StructField) -> bool>(&self, predicate: F) -> Vec<syn::Type> {
self.fields_with(predicate)
.map(|field| field.data.ty.clone())
.collect::<Vec<_>>()
}

/// Get a collection of all active types
/// Get a collection of types which are exposed to either the scene serialization or reflection API
pub fn active_types(&self) -> Vec<syn::Type> {
self.active_fields()
.map(|field| field.data.ty.clone())
.collect::<Vec<_>>()
self.types_with(|field| field.attrs.ignore != ReflectFieldIgnore::IgnoreAlways)
}

/// Get a collection of fields which are exposed to the reflection API
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
self.fields_with(|field| field.attrs.ignore != ReflectFieldIgnore::IgnoreAlways)
}

/// Get a collection of fields which are ignored from the reflection API
pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
self.fields_with(|field| field.attrs.ignore != ReflectFieldIgnore::None)
}

/// The [`DeriveType`] of this struct.
Expand Down Expand Up @@ -187,11 +200,25 @@ impl<'a> ReflectDeriveData<'a> {

/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
pub fn get_type_registration(&self) -> proc_macro2::TokenStream {
let mut idxs = Vec::default();
self.fields.iter().fold(0, |next_idx, field| {
if field.attrs.ignore == ReflectFieldIgnore::IgnoreAlways {
idxs.push(next_idx);
next_idx
} else if field.attrs.ignore == ReflectFieldIgnore::IgnoreSerialization {
idxs.push(next_idx);
next_idx + 1
} else {
next_idx + 1
}
});

crate::registration::impl_get_type_registration(
self.type_name,
&self.bevy_reflect_path,
self.traits.idents(),
self.generics,
&idxs,
)
}
}
32 changes: 26 additions & 6 deletions crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,28 @@ use quote::ToTokens;
use syn::spanned::Spanned;
use syn::{Attribute, Lit, Meta, NestedMeta};

pub(crate) static IGNORE_ATTR: &str = "ignore";
pub(crate) static IGNORE_SERIALIZATION_ATTR: &str = "skip_serializing";
pub(crate) static IGNORE_ALL_ATTR: &str = "ignore";

pub(crate) static DEFAULT_ATTR: &str = "default";

/// Stores data about if the field should be visible via the Reflect and serialization interfaces
#[derive(Default, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ReflectFieldIgnore {
/// Don't ignore, appear to all systems
#[default]
None,
/// Ignore when serializing but not when reflecting
IgnoreSerialization,
/// Ignore for all systems
IgnoreAlways,
}

/// A container for attributes defined on a reflected type's field.
#[derive(Default)]
pub(crate) struct ReflectFieldAttr {
/// Determines if this field should be ignored.
pub ignore: bool,
/// Determines how this field should be ignored if at all.
pub ignore: ReflectFieldIgnore,
/// Sets the default behavior of this field.
pub default: DefaultBehavior,
}
Expand Down Expand Up @@ -65,9 +79,15 @@ pub(crate) fn parse_field_attrs(attrs: &[Attribute]) -> Result<ReflectFieldAttr,
/// Recursively parses attribute metadata for things like `#[reflect(ignore)]` and `#[reflect(default = "foo")]`
fn parse_meta(args: &mut ReflectFieldAttr, meta: &Meta) -> Result<(), syn::Error> {
match meta {
Meta::Path(path) if path.is_ident(IGNORE_ATTR) => {
args.ignore = true;
Ok(())
Meta::Path(path) if path.is_ident(IGNORE_SERIALIZATION_ATTR) => {
(args.ignore == ReflectFieldIgnore::None)
.then(|| args.ignore = ReflectFieldIgnore::IgnoreSerialization)
.ok_or_else(|| syn::Error::new_spanned(path, format!("Only one of ['{IGNORE_SERIALIZATION_ATTR}','{IGNORE_ALL_ATTR}'] is allowed")))
}
Meta::Path(path) if path.is_ident(IGNORE_ALL_ATTR) => {
(args.ignore == ReflectFieldIgnore::None)
.then(|| args.ignore = ReflectFieldIgnore::IgnoreAlways)
.ok_or_else(|| syn::Error::new_spanned(path, format!("Only one of ['{IGNORE_SERIALIZATION_ATTR}','{IGNORE_ALL_ATTR}'] is allowed")))
}
Meta::Path(path) if path.is_ident(DEFAULT_ATTR) => {
args.default = DefaultBehavior::Default;
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ fn impl_struct_internal(derive_data: &ReflectDeriveData, is_tuple: bool) -> Toke
Ident::new("Struct", Span::call_site())
};

// types which at some point may be reachable via reflection or serialization
let field_types = derive_data.active_types();

let MemberValuePair(active_members, active_values) =
get_active_fields(derive_data, &ref_struct, &ref_struct_type, is_tuple);

Expand Down
8 changes: 4 additions & 4 deletions crates/bevy_reflect/bevy_reflect_derive/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,8 @@ pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream {
.unwrap_or_else(|| Member::Unnamed(Index::from(field.index)))
})
.collect::<Vec<_>>();
let field_types = derive_data
.active_fields()
.map(|field| field.data.ty.clone())
.collect::<Vec<_>>();
let field_types = derive_data.active_types();

let field_count = field_idents.len();
let field_indices = (0..field_count).collect::<Vec<usize>>();

Expand Down Expand Up @@ -210,10 +208,12 @@ pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream
.active_fields()
.map(|field| Member::Unnamed(Index::from(field.index)))
.collect::<Vec<_>>();

let field_types = derive_data
.active_fields()
.map(|field| field.data.ty.clone())
.collect::<Vec<_>>();

let field_count = field_idents.len();
let field_indices = (0..field_count).collect::<Vec<usize>>();

Expand Down
1 change: 1 addition & 0 deletions crates/bevy_reflect/bevy_reflect_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ pub fn impl_reflect_value(input: TokenStream) -> TokenStream {
&bevy_reflect_path,
registration_data,
&reflect_value_def.generics,
&[],
);
impls::impl_value(
ty,
Expand Down
3 changes: 3 additions & 0 deletions crates/bevy_reflect/bevy_reflect_derive/src/registration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub(crate) fn impl_get_type_registration(
bevy_reflect_path: &Path,
registration_data: &[Ident],
generics: &Generics,
scene_ignored_field_indices: &[usize],
) -> proc_macro2::TokenStream {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
quote! {
Expand All @@ -18,6 +19,8 @@ pub(crate) fn impl_get_type_registration(
fn get_type_registration() -> #bevy_reflect_path::TypeRegistration {
let mut registration = #bevy_reflect_path::TypeRegistration::of::<#type_name #ty_generics>();
#(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)*
let serialization_data = #bevy_reflect_path::SerializationData::new([#(#scene_ignored_field_indices),*].into_iter());
registration.set_serialization_data(serialization_data);
registration
}
}
Expand Down
90 changes: 90 additions & 0 deletions crates/bevy_reflect/src/serde/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,93 @@ pub(crate) mod type_fields {
pub const ARRAY: &str = "array";
pub const VALUE: &str = "value";
}

#[cfg(test)]
mod tests {
use crate::{self as bevy_reflect, DynamicTupleStruct};
use crate::{
serde::{ReflectDeserializer, ReflectSerializer},
type_registry::{GetTypeRegistration, TypeRegistry},
DynamicStruct, Reflect,
};
use serde::de::DeserializeSeed;

#[test]
fn test_serialization_struct() {
#[derive(Debug, Reflect, PartialEq)]
#[reflect(PartialEq)]
struct TestStruct {
a: i32,
#[reflect(ignore)]
b: i32,
#[reflect(skip_serializing)]
c: i32,
d: i32,
}

let mut registry = TypeRegistry::default();
let registration = TestStruct::get_type_registration();
registry.add_registration(registration);

let test_struct = TestStruct {
a: 3,
b: 4,
c: 5,
d: 6,
};

let serializer = ReflectSerializer::new(&test_struct, &registry);
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 = ReflectDeserializer::new(&registry);
let value = reflect_deserializer.deserialize(&mut deserializer).unwrap();
let deserialized = value.take::<DynamicStruct>().unwrap();

assert!(
expected.reflect_partial_eq(&deserialized).unwrap(),
"Expected {expected:?} found {deserialized:?}"
);
}

#[test]
fn test_serialization_tuple_struct() {
#[derive(Debug, Reflect, PartialEq)]
#[reflect(PartialEq)]
struct TestStruct(
i32,
#[reflect(ignore)] i32,
#[reflect(skip_serializing)] i32,
i32,
);

let mut registry = TypeRegistry::default();
let registration = TestStruct::get_type_registration();
registry.add_registration(registration);

let test_struct = TestStruct(3, 4, 5, 6);

let serializer = ReflectSerializer::new(&test_struct, &registry);
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 = ReflectDeserializer::new(&registry);
let value = reflect_deserializer.deserialize(&mut deserializer).unwrap();
let deserialized = value.take::<DynamicTupleStruct>().unwrap();

assert!(
expected.reflect_partial_eq(&deserialized).unwrap(),
"Expected {expected:?} found {deserialized:?}"
);
}
}
25 changes: 24 additions & 1 deletion crates/bevy_reflect/src/serde/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,18 @@ impl<'a> Serialize for StructValueSerializer<'a> {
S: serde::Serializer,
{
let mut state = serializer.serialize_map(Some(self.struct_value.field_len()))?;
let data = self
.registry
.get_with_name(self.struct_value.type_name())
.and_then(|registration| registration.serialization_data());

for (index, value) in self.struct_value.iter_fields().enumerate() {
if data
.map(|data| data.is_ignored_index(index))
.unwrap_or(false)
{
continue;
}
let key = self.struct_value.name_at(index).unwrap();
state.serialize_entry(key, &ReflectSerializer::new(value, self.registry))?;
}
Expand Down Expand Up @@ -191,7 +202,18 @@ impl<'a> Serialize for TupleStructValueSerializer<'a> {
S: serde::Serializer,
{
let mut state = serializer.serialize_seq(Some(self.tuple_struct.field_len()))?;
for value in self.tuple_struct.iter_fields() {
let data = self
.registry
.get_with_name(self.tuple_struct.type_name())
.and_then(|registration| registration.serialization_data());

for (index, value) in self.tuple_struct.iter_fields().enumerate() {
if data
.map(|data| data.is_ignored_index(index))
.unwrap_or(false)
{
continue;
}
state.serialize_element(&ReflectSerializer::new(value, self.registry))?;
}
state.end()
Expand Down Expand Up @@ -233,6 +255,7 @@ impl<'a> Serialize for TupleValueSerializer<'a> {
S: serde::Serializer,
{
let mut state = serializer.serialize_seq(Some(self.tuple.field_len()))?;

for value in self.tuple.iter_fields() {
state.serialize_element(&ReflectSerializer::new(value, self.registry))?;
}
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_reflect/src/struct_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub trait Struct: Reflect {
/// Returns the number of fields in the struct.
fn field_len(&self) -> usize;

/// Returns an iterator over the values of the struct's fields.
/// Returns an iterator over the values of the reflectable fields for this struct.
fn iter_fields(&self) -> FieldIter;

/// Clones the struct into a [`DynamicStruct`].
Expand Down
Loading

0 comments on commit cf9b287

Please sign in to comment.