Skip to content

Commit 0dcd511

Browse files
makspllItsDoot
authored andcommitted
Add reflect(skip_serializing) which retains reflection but disables automatic serialization (bevyengine#5250)
# Objective - To address problems outlined in bevyengine#5245 ## Solution - Introduce `reflect(skip_serializing)` on top of `reflect(ignore)` which disables automatic serialisation to scenes, but does not disable reflection of the field. --- ## Changelog - Adds: - `bevy_reflect::serde::type_data` module - `SerializationData` structure for describing which fields are to be/not to be ignored, automatically registers as type_data for struct-based types - the `skip_serialization` flag for `#[reflect(...)]` - Removes: - ability to ignore Enum variants in serialization, since that didn't work anyway ## Migration Guide - Change `#[reflect(ignore)]` to `#[reflect(skip_serializing)]` where disabling reflection is not the intended effect. - Remove ignore/skip attributes from enum variants as these won't do anything anymore
1 parent 4cfb369 commit 0dcd511

18 files changed

+330
-74
lines changed

crates/bevy_reflect/bevy_reflect_derive/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ syn = { version = "1.0", features = ["full"] }
1818
proc-macro2 = "1.0"
1919
quote = "1.0"
2020
uuid = { version = "1.1", features = ["v4"] }
21+
bit-set = "0.5.2"

crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs

Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
use crate::container_attributes::ReflectTraits;
22
use crate::field_attributes::{parse_field_attrs, ReflectFieldAttr};
3+
use crate::utility::members_to_serialization_denylist;
4+
use bit_set::BitSet;
35
use quote::quote;
46

57
use crate::{utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME};
68
use syn::punctuated::Punctuated;
79
use syn::spanned::Spanned;
8-
use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Token, Type, Variant};
10+
use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Token, Variant};
911

1012
pub(crate) enum ReflectDerive<'a> {
1113
Struct(ReflectStruct<'a>),
@@ -54,6 +56,7 @@ pub(crate) struct ReflectMeta<'a> {
5456
/// ```
5557
pub(crate) struct ReflectStruct<'a> {
5658
meta: ReflectMeta<'a>,
59+
serialization_denylist: BitSet<u32>,
5760
fields: Vec<StructField<'a>>,
5861
}
5962

@@ -92,6 +95,7 @@ pub(crate) struct EnumVariant<'a> {
9295
/// The fields within this variant.
9396
pub fields: EnumVariantFields<'a>,
9497
/// The reflection-based attributes on the variant.
98+
#[allow(dead_code)]
9599
pub attrs: ReflectFieldAttr,
96100
/// The index of this variant within the enum.
97101
#[allow(dead_code)]
@@ -125,18 +129,24 @@ impl<'a> ReflectDerive<'a> {
125129
_ => continue,
126130
}
127131
}
128-
129-
let meta = ReflectMeta::new(&input.ident, &input.generics, traits);
130-
131132
if force_reflect_value {
132-
return Ok(Self::Value(meta));
133+
return Ok(Self::Value(ReflectMeta::new(
134+
&input.ident,
135+
&input.generics,
136+
traits,
137+
)));
133138
}
134139

135140
return match &input.data {
136141
Data::Struct(data) => {
142+
let fields = Self::collect_struct_fields(&data.fields)?;
143+
let meta = ReflectMeta::new(&input.ident, &input.generics, traits);
137144
let reflect_struct = ReflectStruct {
138145
meta,
139-
fields: Self::collect_struct_fields(&data.fields)?,
146+
serialization_denylist: members_to_serialization_denylist(
147+
fields.iter().map(|v| v.attrs.ignore),
148+
),
149+
fields,
140150
};
141151

142152
match data.fields {
@@ -146,10 +156,10 @@ impl<'a> ReflectDerive<'a> {
146156
}
147157
}
148158
Data::Enum(data) => {
149-
let reflect_enum = ReflectEnum {
150-
meta,
151-
variants: Self::collect_enum_variants(&data.variants)?,
152-
};
159+
let variants = Self::collect_enum_variants(&data.variants)?;
160+
let meta = ReflectMeta::new(&input.ident, &input.generics, traits);
161+
162+
let reflect_enum = ReflectEnum { meta, variants };
153163
Ok(Self::Enum(reflect_enum))
154164
}
155165
Data::Union(..) => Err(syn::Error::new(
@@ -246,6 +256,7 @@ impl<'a> ReflectMeta<'a> {
246256
&self.bevy_reflect_path,
247257
self.traits.idents(),
248258
self.generics,
259+
None,
249260
)
250261
}
251262
}
@@ -256,19 +267,50 @@ impl<'a> ReflectStruct<'a> {
256267
&self.meta
257268
}
258269

259-
/// Get an iterator over the active fields.
260-
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
261-
self.fields.iter().filter(|field| !field.attrs.ignore)
270+
/// Access the data about which fields should be ignored during serialization.
271+
///
272+
/// The returned bitset is a collection of indices obtained from the [`members_to_serialization_denylist`](crate::utility::members_to_serialization_denylist) function.
273+
#[allow(dead_code)]
274+
pub fn serialization_denylist(&self) -> &BitSet<u32> {
275+
&self.serialization_denylist
262276
}
263277

264-
/// Get an iterator over the ignored fields.
265-
pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
266-
self.fields.iter().filter(|field| field.attrs.ignore)
278+
/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
279+
///
280+
/// Returns a specific implementation for structs and this method should be preffered over the generic [`get_type_registration`](crate::ReflectMeta) method
281+
pub fn get_type_registration(&self) -> proc_macro2::TokenStream {
282+
let reflect_path = self.meta.bevy_reflect_path();
283+
284+
crate::registration::impl_get_type_registration(
285+
self.meta.type_name(),
286+
reflect_path,
287+
self.meta.traits().idents(),
288+
self.meta.generics(),
289+
Some(&self.serialization_denylist),
290+
)
267291
}
268292

269-
/// Get a collection of all active types.
270-
pub fn active_types(&self) -> impl Iterator<Item = &Type> {
271-
self.active_fields().map(|field| &field.data.ty)
293+
/// Get a collection of types which are exposed to the reflection API
294+
pub fn active_types(&self) -> Vec<syn::Type> {
295+
self.fields
296+
.iter()
297+
.filter(move |field| field.attrs.ignore.is_active())
298+
.map(|field| field.data.ty.clone())
299+
.collect::<Vec<_>>()
300+
}
301+
302+
/// Get an iterator of fields which are exposed to the reflection API
303+
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
304+
self.fields
305+
.iter()
306+
.filter(move |field| field.attrs.ignore.is_active())
307+
}
308+
309+
/// Get an iterator of fields which are ignored by the reflection API
310+
pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
311+
self.fields
312+
.iter()
313+
.filter(move |field| field.attrs.ignore.is_ignored())
272314
}
273315

274316
/// The complete set of fields in this struct.
@@ -284,17 +326,6 @@ impl<'a> ReflectEnum<'a> {
284326
&self.meta
285327
}
286328

287-
/// Get an iterator over the active variants.
288-
pub fn active_variants(&self) -> impl Iterator<Item = &EnumVariant<'a>> {
289-
self.variants.iter().filter(|variant| !variant.attrs.ignore)
290-
}
291-
292-
/// Get an iterator over the ignored variants.
293-
#[allow(dead_code)]
294-
pub fn ignored_variants(&self) -> impl Iterator<Item = &EnumVariant<'a>> {
295-
self.variants.iter().filter(|variant| variant.attrs.ignore)
296-
}
297-
298329
/// Returns the given ident as a qualified unit variant of this enum.
299330
pub fn get_unit(&self, variant: &Ident) -> proc_macro2::TokenStream {
300331
let name = self.meta.type_name;
@@ -304,7 +335,6 @@ impl<'a> ReflectEnum<'a> {
304335
}
305336

306337
/// The complete set of variants in this enum.
307-
#[allow(dead_code)]
308338
pub fn variants(&self) -> &[EnumVariant<'a>] {
309339
&self.variants
310340
}

crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub(crate) fn get_variant_constructors(
2424
let mut variant_names = Vec::with_capacity(variant_count);
2525
let mut variant_constructors = Vec::with_capacity(variant_count);
2626

27-
for variant in reflect_enum.active_variants() {
27+
for variant in reflect_enum.variants() {
2828
let ident = &variant.data.ident;
2929
let name = ident.to_string();
3030
let variant_constructor = reflect_enum.get_unit(ident);
@@ -38,7 +38,7 @@ pub(crate) fn get_variant_constructors(
3838
let mut reflect_index: usize = 0;
3939
let constructor_fields = fields.iter().enumerate().map(|(declar_index, field)| {
4040
let field_ident = ident_or_index(field.data.ident.as_ref(), declar_index);
41-
let field_value = if field.attrs.ignore {
41+
let field_value = if field.attrs.ignore.is_ignored() {
4242
quote! { Default::default() }
4343
} else {
4444
let error_repr = field.data.ident.as_ref().map_or_else(

crates/bevy_reflect/bevy_reflect_derive/src/field_attributes.rs

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,47 @@ use quote::ToTokens;
99
use syn::spanned::Spanned;
1010
use syn::{Attribute, Lit, Meta, NestedMeta};
1111

12-
pub(crate) static IGNORE_ATTR: &str = "ignore";
12+
pub(crate) static IGNORE_SERIALIZATION_ATTR: &str = "skip_serializing";
13+
pub(crate) static IGNORE_ALL_ATTR: &str = "ignore";
14+
1315
pub(crate) static DEFAULT_ATTR: &str = "default";
1416

17+
/// Stores data about if the field should be visible via the Reflect and serialization interfaces
18+
///
19+
/// Note the relationship between serialization and reflection is such that a member must be reflected in order to be serialized.
20+
/// In boolean logic this is described as: `is_serialized -> is_reflected`, this means we can reflect something without serializing it but not the other way round.
21+
/// The `is_reflected` predicate is provided as `self.is_active()`
22+
#[derive(Default, Clone, Copy, PartialEq, Eq)]
23+
pub(crate) enum ReflectIgnoreBehavior {
24+
/// Don't ignore, appear to all systems
25+
#[default]
26+
None,
27+
/// Ignore when serializing but not when reflecting
28+
IgnoreSerialization,
29+
/// Ignore both when serializing and reflecting
30+
IgnoreAlways,
31+
}
32+
33+
impl ReflectIgnoreBehavior {
34+
/// Returns `true` if the ignoring behaviour implies member is included in the reflection API, and false otherwise.
35+
pub fn is_active(self) -> bool {
36+
match self {
37+
ReflectIgnoreBehavior::None | ReflectIgnoreBehavior::IgnoreSerialization => true,
38+
ReflectIgnoreBehavior::IgnoreAlways => false,
39+
}
40+
}
41+
42+
/// The exact logical opposite of `self.is_active()` returns true iff this member is not part of the reflection API whatsover (neither serialized nor reflected)
43+
pub fn is_ignored(self) -> bool {
44+
!self.is_active()
45+
}
46+
}
47+
1548
/// A container for attributes defined on a reflected type's field.
1649
#[derive(Default)]
1750
pub(crate) struct ReflectFieldAttr {
18-
/// Determines if this field should be ignored.
19-
pub ignore: bool,
51+
/// Determines how this field should be ignored if at all.
52+
pub ignore: ReflectIgnoreBehavior,
2053
/// Sets the default behavior of this field.
2154
pub default: DefaultBehavior,
2255
}
@@ -65,9 +98,15 @@ pub(crate) fn parse_field_attrs(attrs: &[Attribute]) -> Result<ReflectFieldAttr,
6598
/// Recursively parses attribute metadata for things like `#[reflect(ignore)]` and `#[reflect(default = "foo")]`
6699
fn parse_meta(args: &mut ReflectFieldAttr, meta: &Meta) -> Result<(), syn::Error> {
67100
match meta {
68-
Meta::Path(path) if path.is_ident(IGNORE_ATTR) => {
69-
args.ignore = true;
70-
Ok(())
101+
Meta::Path(path) if path.is_ident(IGNORE_SERIALIZATION_ATTR) => {
102+
(args.ignore == ReflectIgnoreBehavior::None)
103+
.then(|| args.ignore = ReflectIgnoreBehavior::IgnoreSerialization)
104+
.ok_or_else(|| syn::Error::new_spanned(path, format!("Only one of ['{IGNORE_SERIALIZATION_ATTR}','{IGNORE_ALL_ATTR}'] is allowed")))
105+
}
106+
Meta::Path(path) if path.is_ident(IGNORE_ALL_ATTR) => {
107+
(args.ignore == ReflectIgnoreBehavior::None)
108+
.then(|| args.ignore = ReflectIgnoreBehavior::IgnoreAlways)
109+
.ok_or_else(|| syn::Error::new_spanned(path, format!("Only one of ['{IGNORE_SERIALIZATION_ATTR}','{IGNORE_ALL_ATTR}'] is allowed")))
71110
}
72111
Meta::Path(path) if path.is_ident(DEFAULT_ATTR) => {
73112
args.default = DefaultBehavior::Default;

crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden
269269
let mut enum_variant_name = Vec::new();
270270
let mut enum_variant_type = Vec::new();
271271

272-
for variant in reflect_enum.active_variants() {
272+
for variant in reflect_enum.variants() {
273273
let ident = &variant.data.ident;
274274
let name = ident.to_string();
275275
let unit = reflect_enum.get_unit(ident);
@@ -281,7 +281,7 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden
281281
let mut constructor_argument = Vec::new();
282282
let mut reflect_idx = 0;
283283
for field in fields.iter() {
284-
if field.attrs.ignore {
284+
if field.attrs.ignore.is_ignored() {
285285
// Ignored field
286286
continue;
287287
}

crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream {
6464
bevy_reflect_path,
6565
);
6666

67-
let get_type_registration_impl = reflect_struct.meta().get_type_registration();
67+
let get_type_registration_impl = reflect_struct.get_type_registration();
6868
let (impl_generics, ty_generics, where_clause) =
6969
reflect_struct.meta().generics().split_for_impl();
7070

crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use syn::{Index, Member};
88
pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream {
99
let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();
1010
let struct_name = reflect_struct.meta().type_name();
11-
let get_type_registration_impl = reflect_struct.meta().get_type_registration();
11+
let get_type_registration_impl = reflect_struct.get_type_registration();
1212

1313
let field_idents = reflect_struct
1414
.active_fields()

crates/bevy_reflect/bevy_reflect_derive/src/registration.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Contains code related specifically to Bevy's type registration.
22
3+
use bit_set::BitSet;
34
use proc_macro2::Ident;
45
use quote::quote;
56
use syn::{Generics, Path};
@@ -10,14 +11,24 @@ pub(crate) fn impl_get_type_registration(
1011
bevy_reflect_path: &Path,
1112
registration_data: &[Ident],
1213
generics: &Generics,
14+
serialization_denylist: Option<&BitSet<u32>>,
1315
) -> proc_macro2::TokenStream {
1416
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
17+
let serialization_data = serialization_denylist.map(|denylist| {
18+
let denylist = denylist.into_iter().map(|v| v as usize);
19+
quote! {
20+
let ignored_indices = [#(#denylist),*].into_iter();
21+
registration.insert::<#bevy_reflect_path::serde::SerializationData>(#bevy_reflect_path::serde::SerializationData::new(ignored_indices));
22+
}
23+
});
24+
1525
quote! {
1626
#[allow(unused_mut)]
1727
impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_clause {
1828
fn get_type_registration() -> #bevy_reflect_path::TypeRegistration {
1929
let mut registration = #bevy_reflect_path::TypeRegistration::of::<#type_name #ty_generics>();
2030
registration.insert::<#bevy_reflect_path::ReflectFromPtr>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());
31+
#serialization_data
2132
#(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)*
2233
registration
2334
}

crates/bevy_reflect/bevy_reflect_derive/src/utility.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! General-purpose utility functions for internal usage within this crate.
22
3+
use crate::field_attributes::ReflectIgnoreBehavior;
34
use bevy_macro_utils::BevyManifest;
5+
use bit_set::BitSet;
46
use proc_macro2::{Ident, Span};
57
use syn::{Member, Path};
68

@@ -96,3 +98,42 @@ impl<T> ResultSifter<T> {
9698
}
9799
}
98100
}
101+
102+
/// Converts an iterator over ignore behaviour of members to a bitset of ignored members.
103+
///
104+
/// Takes into account the fact that always ignored (non-reflected) members are skipped.
105+
///
106+
/// # Example
107+
/// ```rust,ignore
108+
/// pub struct HelloWorld {
109+
/// reflected_field: u32 // index: 0
110+
///
111+
/// #[reflect(ignore)]
112+
/// non_reflected_field: u32 // index: N/A (not 1!)
113+
///
114+
/// #[reflect(skip_serializing)]
115+
/// non_serialized_field: u32 // index: 1
116+
/// }
117+
/// ```
118+
/// Would convert to the `0b01` bitset (i.e second field is NOT serialized)
119+
///
120+
pub(crate) fn members_to_serialization_denylist<T>(member_iter: T) -> BitSet<u32>
121+
where
122+
T: Iterator<Item = ReflectIgnoreBehavior>,
123+
{
124+
let mut bitset = BitSet::default();
125+
126+
member_iter.fold(0, |next_idx, member| match member {
127+
ReflectIgnoreBehavior::IgnoreAlways => {
128+
bitset.insert(next_idx);
129+
next_idx
130+
}
131+
ReflectIgnoreBehavior::IgnoreSerialization => {
132+
bitset.insert(next_idx);
133+
next_idx + 1
134+
}
135+
ReflectIgnoreBehavior::None => next_idx + 1,
136+
});
137+
138+
bitset
139+
}

0 commit comments

Comments
 (0)