Skip to content

Commit 97f7a1a

Browse files
committed
bevy_reflect: Binary formats (#6140)
# Objective Closes #5934 Currently it is not possible to de/serialize data to non-self-describing formats using reflection. ## Solution Add support for non-self-describing de/serialization using reflection. This allows us to use binary formatters, like [`postcard`](https://crates.io/crates/postcard): ```rust #[derive(Reflect, FromReflect, Debug, PartialEq)] struct Foo { data: String } let mut registry = TypeRegistry::new(); registry.register::<Foo>(); let input = Foo { data: "Hello world!".to_string() }; // === Serialize! === // let serializer = ReflectSerializer::new(&input, &registry); let bytes: Vec<u8> = postcard::to_allocvec(&serializer).unwrap(); println!("{:?}", bytes); // Output: [129, 217, 61, 98, ...] // === Deserialize! === // let deserializer = UntypedReflectDeserializer::new(&registry); let dynamic_output = deserializer .deserialize(&mut postcard::Deserializer::from_bytes(&bytes)) .unwrap(); let output = <Foo as FromReflect>::from_reflect(dynamic_output.as_ref()).unwrap(); assert_eq!(expected, output); // OK! ``` #### Crates Tested - ~~[`rmp-serde`](https://crates.io/crates/rmp-serde)~~ Apparently, this _is_ self-describing - ~~[`bincode` v2.0.0-rc.1](https://crates.io/crates/bincode/2.0.0-rc.1) (using [this PR](bincode-org/bincode#586 This actually works for the latest release (v1.3.3) of [`bincode`](https://crates.io/crates/bincode) as well. You just need to be sure to use fixed-int encoding. - [`postcard`](https://crates.io/crates/postcard) ## Future Work Ideally, we would refactor the `serde` module, but I don't think I'll do that in this PR so as to keep the diff relatively small (and to avoid any painful rebases). This should probably be done once this is merged, though. Some areas we could improve with a refactor: * Split deserialization logic across multiple files * Consolidate helper functions/structs * Make the logic more DRY --- ## Changelog - Add support for non-self-describing de/serialization using reflection. Co-authored-by: Gino Valente <[email protected]>
1 parent 4c4f476 commit 97f7a1a

File tree

8 files changed

+644
-19
lines changed

8 files changed

+644
-19
lines changed

crates/bevy_reflect/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ glam = { version = "0.21", features = ["serde"], optional = true }
3535

3636
[dev-dependencies]
3737
ron = "0.8.0"
38+
rmp-serde = "1.1"
39+
bincode = "1.3"
3840

3941
[[example]]
4042
name = "reflect_docs"

crates/bevy_reflect/src/enums/enum_trait.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ pub struct EnumInfo {
136136
type_name: &'static str,
137137
type_id: TypeId,
138138
variants: Box<[VariantInfo]>,
139+
variant_names: Box<[&'static str]>,
139140
variant_indices: HashMap<&'static str, usize>,
140141
#[cfg(feature = "documentation")]
141142
docs: Option<&'static str>,
@@ -156,11 +157,14 @@ impl EnumInfo {
156157
.map(|(index, variant)| (variant.name(), index))
157158
.collect::<HashMap<_, _>>();
158159

160+
let variant_names = variants.iter().map(|variant| variant.name()).collect();
161+
159162
Self {
160163
name,
161164
type_name: std::any::type_name::<TEnum>(),
162165
type_id: TypeId::of::<TEnum>(),
163166
variants: variants.to_vec().into_boxed_slice(),
167+
variant_names,
164168
variant_indices,
165169
#[cfg(feature = "documentation")]
166170
docs: None,
@@ -173,6 +177,11 @@ impl EnumInfo {
173177
Self { docs, ..self }
174178
}
175179

180+
/// A slice containing the names of all variants in order.
181+
pub fn variant_names(&self) -> &[&'static str] {
182+
&self.variant_names
183+
}
184+
176185
/// Get a variant with the given name.
177186
pub fn variant(&self, name: &str) -> Option<&VariantInfo> {
178187
self.variant_indices

crates/bevy_reflect/src/enums/variants.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ impl VariantInfo {
8989
pub struct StructVariantInfo {
9090
name: &'static str,
9191
fields: Box<[NamedField]>,
92+
field_names: Box<[&'static str]>,
9293
field_indices: HashMap<&'static str, usize>,
9394
#[cfg(feature = "documentation")]
9495
docs: Option<&'static str>,
@@ -98,9 +99,11 @@ impl StructVariantInfo {
9899
/// Create a new [`StructVariantInfo`].
99100
pub fn new(name: &'static str, fields: &[NamedField]) -> Self {
100101
let field_indices = Self::collect_field_indices(fields);
102+
let field_names = fields.iter().map(|field| field.name()).collect();
101103
Self {
102104
name,
103105
fields: fields.to_vec().into_boxed_slice(),
106+
field_names,
104107
field_indices,
105108
#[cfg(feature = "documentation")]
106109
docs: None,
@@ -118,6 +121,11 @@ impl StructVariantInfo {
118121
self.name
119122
}
120123

124+
/// A slice containing the names of all fields in order.
125+
pub fn field_names(&self) -> &[&'static str] {
126+
&self.field_names
127+
}
128+
121129
/// Get the field with the given name.
122130
pub fn field(&self, name: &str) -> Option<&NamedField> {
123131
self.field_indices

0 commit comments

Comments
 (0)