mirror of
https://github.com/bevyengine/bevy
synced 2024-11-29 16:10:19 +00:00
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, ®istry); let bytes: Vec<u8> = postcard::to_allocvec(&serializer).unwrap(); println!("{:?}", bytes); // Output: [129, 217, 61, 98, ...] // === Deserialize! === // let deserializer = UntypedReflectDeserializer::new(®istry); 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](https://github.com/bincode-org/bincode/pull/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 <49806985+MrGVSV@users.noreply.github.com>
This commit is contained in:
parent
4c4f47697c
commit
97f7a1a99c
8 changed files with 644 additions and 19 deletions
|
@ -35,6 +35,8 @@ glam = { version = "0.21", features = ["serde"], optional = true }
|
|||
|
||||
[dev-dependencies]
|
||||
ron = "0.8.0"
|
||||
rmp-serde = "1.1"
|
||||
bincode = "1.3"
|
||||
|
||||
[[example]]
|
||||
name = "reflect_docs"
|
||||
|
|
|
@ -136,6 +136,7 @@ pub struct EnumInfo {
|
|||
type_name: &'static str,
|
||||
type_id: TypeId,
|
||||
variants: Box<[VariantInfo]>,
|
||||
variant_names: Box<[&'static str]>,
|
||||
variant_indices: HashMap<&'static str, usize>,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Option<&'static str>,
|
||||
|
@ -156,11 +157,14 @@ impl EnumInfo {
|
|||
.map(|(index, variant)| (variant.name(), index))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let variant_names = variants.iter().map(|variant| variant.name()).collect();
|
||||
|
||||
Self {
|
||||
name,
|
||||
type_name: std::any::type_name::<TEnum>(),
|
||||
type_id: TypeId::of::<TEnum>(),
|
||||
variants: variants.to_vec().into_boxed_slice(),
|
||||
variant_names,
|
||||
variant_indices,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: None,
|
||||
|
@ -173,6 +177,11 @@ impl EnumInfo {
|
|||
Self { docs, ..self }
|
||||
}
|
||||
|
||||
/// A slice containing the names of all variants in order.
|
||||
pub fn variant_names(&self) -> &[&'static str] {
|
||||
&self.variant_names
|
||||
}
|
||||
|
||||
/// Get a variant with the given name.
|
||||
pub fn variant(&self, name: &str) -> Option<&VariantInfo> {
|
||||
self.variant_indices
|
||||
|
|
|
@ -89,6 +89,7 @@ impl VariantInfo {
|
|||
pub struct StructVariantInfo {
|
||||
name: &'static str,
|
||||
fields: Box<[NamedField]>,
|
||||
field_names: Box<[&'static str]>,
|
||||
field_indices: HashMap<&'static str, usize>,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Option<&'static str>,
|
||||
|
@ -98,9 +99,11 @@ impl StructVariantInfo {
|
|||
/// Create a new [`StructVariantInfo`].
|
||||
pub fn new(name: &'static str, fields: &[NamedField]) -> Self {
|
||||
let field_indices = Self::collect_field_indices(fields);
|
||||
let field_names = fields.iter().map(|field| field.name()).collect();
|
||||
Self {
|
||||
name,
|
||||
fields: fields.to_vec().into_boxed_slice(),
|
||||
field_names,
|
||||
field_indices,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: None,
|
||||
|
@ -118,6 +121,11 @@ impl StructVariantInfo {
|
|||
self.name
|
||||
}
|
||||
|
||||
/// A slice containing the names of all fields in order.
|
||||
pub fn field_names(&self) -> &[&'static str] {
|
||||
&self.field_names
|
||||
}
|
||||
|
||||
/// Get the field with the given name.
|
||||
pub fn field(&self, name: &str) -> Option<&NamedField> {
|
||||
self.field_indices
|
||||
|
|
|
@ -35,6 +35,14 @@ trait TupleLikeInfo {
|
|||
fn get_field_len(&self) -> usize;
|
||||
}
|
||||
|
||||
trait Container {
|
||||
fn get_field_registration<'a, E: Error>(
|
||||
&self,
|
||||
index: usize,
|
||||
registry: &'a TypeRegistry,
|
||||
) -> Result<&'a TypeRegistration, E>;
|
||||
}
|
||||
|
||||
impl StructLikeInfo for StructInfo {
|
||||
fn get_name(&self) -> &str {
|
||||
self.type_name()
|
||||
|
@ -49,6 +57,23 @@ impl StructLikeInfo for StructInfo {
|
|||
}
|
||||
}
|
||||
|
||||
impl Container for StructInfo {
|
||||
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 struct {}",
|
||||
index,
|
||||
self.type_name(),
|
||||
))
|
||||
})?;
|
||||
get_registration(field.type_id(), field.type_name(), registry)
|
||||
}
|
||||
}
|
||||
|
||||
impl StructLikeInfo for StructVariantInfo {
|
||||
fn get_name(&self) -> &str {
|
||||
self.name()
|
||||
|
@ -63,6 +88,23 @@ impl StructLikeInfo for StructVariantInfo {
|
|||
}
|
||||
}
|
||||
|
||||
impl Container for StructVariantInfo {
|
||||
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 variant {}",
|
||||
index,
|
||||
self.name(),
|
||||
))
|
||||
})?;
|
||||
get_registration(field.type_id(), field.type_name(), registry)
|
||||
}
|
||||
}
|
||||
|
||||
impl TupleLikeInfo for TupleInfo {
|
||||
fn get_name(&self) -> &str {
|
||||
self.type_name()
|
||||
|
@ -151,6 +193,23 @@ impl<'de> Deserialize<'de> for Ident {
|
|||
}
|
||||
}
|
||||
|
||||
struct U32Visitor;
|
||||
|
||||
impl<'de> Visitor<'de> for U32Visitor {
|
||||
type Value = u32;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("u32")
|
||||
}
|
||||
|
||||
fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
|
||||
/// A general purpose deserializer for reflected types.
|
||||
///
|
||||
/// This will return a [`Box<dyn Reflect>`] containing the deserialized data.
|
||||
|
@ -192,7 +251,7 @@ impl<'a, 'de> DeserializeSeed<'de> for UntypedReflectDeserializer<'a> {
|
|||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_any(UntypedReflectDeserializerVisitor {
|
||||
deserializer.deserialize_map(UntypedReflectDeserializerVisitor {
|
||||
registry: self.registry,
|
||||
})
|
||||
}
|
||||
|
@ -279,8 +338,7 @@ impl<'a, 'de> DeserializeSeed<'de> for TypedReflectDeserializer<'a> {
|
|||
TypeInfo::Struct(struct_info) => {
|
||||
let mut dynamic_struct = deserializer.deserialize_struct(
|
||||
struct_info.name(),
|
||||
// Field names are mainly just a hint, we don't necessarily need to store and pass that data
|
||||
&[],
|
||||
struct_info.field_names(),
|
||||
StructVisitor {
|
||||
struct_info,
|
||||
registry: self.registry,
|
||||
|
@ -350,8 +408,7 @@ impl<'a, 'de> DeserializeSeed<'de> for TypedReflectDeserializer<'a> {
|
|||
} else {
|
||||
deserializer.deserialize_enum(
|
||||
enum_info.name(),
|
||||
// Variant names are mainly just a hint, we don't necessarily need to store and pass that data
|
||||
&[],
|
||||
enum_info.variant_names(),
|
||||
EnumVisitor {
|
||||
enum_info,
|
||||
registry: self.registry,
|
||||
|
@ -398,6 +455,30 @@ impl<'a, 'de> Visitor<'de> for StructVisitor<'a> {
|
|||
{
|
||||
visit_struct(&mut map, self.struct_info, 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();
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
struct TupleStructVisitor<'a> {
|
||||
|
@ -609,21 +690,15 @@ impl<'a, 'de> Visitor<'de> for EnumVisitor<'a> {
|
|||
A: EnumAccess<'de>,
|
||||
{
|
||||
let mut dynamic_enum = DynamicEnum::default();
|
||||
let (Ident(variant_name), variant) = data.variant().unwrap();
|
||||
let variant_info = self.enum_info.variant(&variant_name).ok_or_else(|| {
|
||||
let names = self.enum_info.iter().map(|variant| variant.name());
|
||||
Error::custom(format_args!(
|
||||
"unknown variant `{}`, expected one of {:?}",
|
||||
variant_name,
|
||||
ExpectedValues(names.collect())
|
||||
))
|
||||
let (variant_info, variant) = data.variant_seed(VariantDeserializer {
|
||||
enum_info: self.enum_info,
|
||||
})?;
|
||||
|
||||
let value: DynamicVariant = match variant_info {
|
||||
VariantInfo::Unit(..) => variant.unit_variant()?.into(),
|
||||
VariantInfo::Struct(struct_info) => variant
|
||||
.struct_variant(
|
||||
// Field names are mainly just a hint, we don't necessarily need to store and pass that data
|
||||
&[],
|
||||
struct_info.field_names(),
|
||||
StructVariantVisitor {
|
||||
struct_info,
|
||||
registry: self.registry,
|
||||
|
@ -653,11 +728,63 @@ impl<'a, 'de> Visitor<'de> for EnumVisitor<'a> {
|
|||
.into(),
|
||||
};
|
||||
|
||||
dynamic_enum.set_variant(variant_name, value);
|
||||
dynamic_enum.set_variant(variant_info.name(), value);
|
||||
Ok(dynamic_enum)
|
||||
}
|
||||
}
|
||||
|
||||
struct VariantDeserializer {
|
||||
enum_info: &'static EnumInfo,
|
||||
}
|
||||
|
||||
impl<'de> DeserializeSeed<'de> for VariantDeserializer {
|
||||
type Value = &'static VariantInfo;
|
||||
|
||||
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
struct VariantVisitor(&'static EnumInfo);
|
||||
|
||||
impl<'de> Visitor<'de> for VariantVisitor {
|
||||
type Value = &'static VariantInfo;
|
||||
|
||||
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
|
||||
formatter.write_str("expected either a variant index or variant name")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, variant_name: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
self.0.variant(variant_name).ok_or_else(|| {
|
||||
let names = self.0.iter().map(|variant| variant.name());
|
||||
Error::custom(format_args!(
|
||||
"unknown variant `{}`, expected one of {:?}",
|
||||
variant_name,
|
||||
ExpectedValues(names.collect())
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn visit_u32<E>(self, variant_index: u32) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
self.0.variant_at(variant_index as usize).ok_or_else(|| {
|
||||
Error::custom(format_args!(
|
||||
"no variant found at index `{}` on enum `{}`",
|
||||
variant_index,
|
||||
self.0.name()
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_identifier(VariantVisitor(self.enum_info))
|
||||
}
|
||||
}
|
||||
|
||||
struct StructVariantVisitor<'a> {
|
||||
struct_info: &'static StructVariantInfo,
|
||||
registry: &'a TypeRegistry,
|
||||
|
@ -676,6 +803,30 @@ impl<'a, 'de> Visitor<'de> for StructVariantVisitor<'a> {
|
|||
{
|
||||
visit_struct(&mut map, self.struct_info, 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();
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
struct TupleVariantVisitor<'a> {
|
||||
|
@ -836,6 +987,7 @@ fn get_registration<'a, E: Error>(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use bincode::Options;
|
||||
use std::any::TypeId;
|
||||
use std::f32::consts::PI;
|
||||
|
||||
|
@ -1169,4 +1321,111 @@ mod tests {
|
|||
});
|
||||
assert!(expected.reflect_partial_eq(output.as_ref()).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_deserialize_non_self_describing_binary() {
|
||||
let mut map = HashMap::new();
|
||||
map.insert(64, 32);
|
||||
|
||||
let expected = MyStruct {
|
||||
primitive_value: 123,
|
||||
option_value: Some(String::from("Hello world!")),
|
||||
option_value_complex: Some(SomeStruct { foo: 123 }),
|
||||
tuple_value: (PI, 1337),
|
||||
list_value: vec![-2, -1, 0, 1, 2],
|
||||
array_value: [-2, -1, 0, 1, 2],
|
||||
map_value: map,
|
||||
struct_value: SomeStruct { foo: 999999999 },
|
||||
tuple_struct_value: SomeTupleStruct(String::from("Tuple Struct")),
|
||||
unit_enum: SomeEnum::Unit,
|
||||
newtype_enum: SomeEnum::NewType(123),
|
||||
tuple_enum: SomeEnum::Tuple(1.23, 3.21),
|
||||
struct_enum: SomeEnum::Struct {
|
||||
foo: String::from("Struct variant value"),
|
||||
},
|
||||
custom_deserialize: CustomDeserialize {
|
||||
value: 100,
|
||||
inner_struct: SomeDeserializableStruct { foo: 101 },
|
||||
},
|
||||
};
|
||||
|
||||
let registry = get_registry();
|
||||
|
||||
let input = vec![
|
||||
1, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 98, 101, 118, 121, 95, 114, 101, 102,
|
||||
108, 101, 99, 116, 58, 58, 115, 101, 114, 100, 101, 58, 58, 100, 101, 58, 58, 116, 101,
|
||||
115, 116, 115, 58, 58, 77, 121, 83, 116, 114, 117, 99, 116, 123, 1, 12, 0, 0, 0, 0, 0,
|
||||
0, 0, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 1, 123, 0, 0, 0, 0, 0,
|
||||
0, 0, 219, 15, 73, 64, 57, 5, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 254, 255, 255,
|
||||
255, 255, 255, 255, 255, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 254, 255, 255, 255, 255,
|
||||
255, 255, 255, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 64, 32, 0,
|
||||
0, 0, 0, 0, 0, 0, 255, 201, 154, 59, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 84, 117, 112,
|
||||
108, 101, 32, 83, 116, 114, 117, 99, 116, 0, 0, 0, 0, 1, 0, 0, 0, 123, 0, 0, 0, 0, 0,
|
||||
0, 0, 2, 0, 0, 0, 164, 112, 157, 63, 164, 112, 77, 64, 3, 0, 0, 0, 20, 0, 0, 0, 0, 0,
|
||||
0, 0, 83, 116, 114, 117, 99, 116, 32, 118, 97, 114, 105, 97, 110, 116, 32, 118, 97,
|
||||
108, 117, 101, 100, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0,
|
||||
];
|
||||
|
||||
let deserializer = UntypedReflectDeserializer::new(®istry);
|
||||
|
||||
let dynamic_output = bincode::DefaultOptions::new()
|
||||
.with_fixint_encoding()
|
||||
.deserialize_seed(deserializer, &input)
|
||||
.unwrap();
|
||||
|
||||
let output = <MyStruct as FromReflect>::from_reflect(dynamic_output.as_ref()).unwrap();
|
||||
assert_eq!(expected, output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_deserialize_self_describing_binary() {
|
||||
let mut map = HashMap::new();
|
||||
map.insert(64, 32);
|
||||
|
||||
let expected = MyStruct {
|
||||
primitive_value: 123,
|
||||
option_value: Some(String::from("Hello world!")),
|
||||
option_value_complex: Some(SomeStruct { foo: 123 }),
|
||||
tuple_value: (PI, 1337),
|
||||
list_value: vec![-2, -1, 0, 1, 2],
|
||||
array_value: [-2, -1, 0, 1, 2],
|
||||
map_value: map,
|
||||
struct_value: SomeStruct { foo: 999999999 },
|
||||
tuple_struct_value: SomeTupleStruct(String::from("Tuple Struct")),
|
||||
unit_enum: SomeEnum::Unit,
|
||||
newtype_enum: SomeEnum::NewType(123),
|
||||
tuple_enum: SomeEnum::Tuple(1.23, 3.21),
|
||||
struct_enum: SomeEnum::Struct {
|
||||
foo: String::from("Struct variant value"),
|
||||
},
|
||||
custom_deserialize: CustomDeserialize {
|
||||
value: 100,
|
||||
inner_struct: SomeDeserializableStruct { foo: 101 },
|
||||
},
|
||||
};
|
||||
|
||||
let registry = get_registry();
|
||||
|
||||
let input = vec![
|
||||
129, 217, 40, 98, 101, 118, 121, 95, 114, 101, 102, 108, 101, 99, 116, 58, 58, 115,
|
||||
101, 114, 100, 101, 58, 58, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121,
|
||||
83, 116, 114, 117, 99, 116, 158, 123, 172, 72, 101, 108, 108, 111, 32, 119, 111, 114,
|
||||
108, 100, 33, 145, 123, 146, 202, 64, 73, 15, 219, 205, 5, 57, 149, 254, 255, 0, 1, 2,
|
||||
149, 254, 255, 0, 1, 2, 129, 64, 32, 145, 206, 59, 154, 201, 255, 145, 172, 84, 117,
|
||||
112, 108, 101, 32, 83, 116, 114, 117, 99, 116, 164, 85, 110, 105, 116, 129, 167, 78,
|
||||
101, 119, 84, 121, 112, 101, 123, 129, 165, 84, 117, 112, 108, 101, 146, 202, 63, 157,
|
||||
112, 164, 202, 64, 77, 112, 164, 129, 166, 83, 116, 114, 117, 99, 116, 145, 180, 83,
|
||||
116, 114, 117, 99, 116, 32, 118, 97, 114, 105, 97, 110, 116, 32, 118, 97, 108, 117,
|
||||
101, 146, 100, 145, 101,
|
||||
];
|
||||
|
||||
let deserializer = UntypedReflectDeserializer::new(®istry);
|
||||
|
||||
let dynamic_output = deserializer
|
||||
.deserialize(&mut rmp_serde::Deserializer::new(input.as_slice()))
|
||||
.unwrap();
|
||||
|
||||
let output = <MyStruct as FromReflect>::from_reflect(dynamic_output.as_ref()).unwrap();
|
||||
assert_eq!(expected, output);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -533,6 +533,7 @@ mod tests {
|
|||
registry.register::<SomeStruct>();
|
||||
registry.register::<SomeTupleStruct>();
|
||||
registry.register::<CustomSerialize>();
|
||||
registry.register::<SomeEnum>();
|
||||
registry.register::<SomeSerializableStruct>();
|
||||
registry.register_type_data::<SomeSerializableStruct, ReflectSerialize>();
|
||||
registry.register::<String>();
|
||||
|
@ -728,4 +729,102 @@ mod tests {
|
|||
}"#;
|
||||
assert_eq!(expected, output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_serialize_non_self_describing_binary() {
|
||||
let mut map = HashMap::new();
|
||||
map.insert(64, 32);
|
||||
|
||||
let input = MyStruct {
|
||||
primitive_value: 123,
|
||||
option_value: Some(String::from("Hello world!")),
|
||||
option_value_complex: Some(SomeStruct { foo: 123 }),
|
||||
tuple_value: (PI, 1337),
|
||||
list_value: vec![-2, -1, 0, 1, 2],
|
||||
array_value: [-2, -1, 0, 1, 2],
|
||||
map_value: map,
|
||||
struct_value: SomeStruct { foo: 999999999 },
|
||||
tuple_struct_value: SomeTupleStruct(String::from("Tuple Struct")),
|
||||
unit_enum: SomeEnum::Unit,
|
||||
newtype_enum: SomeEnum::NewType(123),
|
||||
tuple_enum: SomeEnum::Tuple(1.23, 3.21),
|
||||
struct_enum: SomeEnum::Struct {
|
||||
foo: String::from("Struct variant value"),
|
||||
},
|
||||
custom_serialize: CustomSerialize {
|
||||
value: 100,
|
||||
inner_struct: SomeSerializableStruct { foo: 101 },
|
||||
},
|
||||
};
|
||||
|
||||
let registry = get_registry();
|
||||
|
||||
let serializer = ReflectSerializer::new(&input, ®istry);
|
||||
let bytes = bincode::serialize(&serializer).unwrap();
|
||||
|
||||
let expected: Vec<u8> = vec![
|
||||
1, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 98, 101, 118, 121, 95, 114, 101, 102,
|
||||
108, 101, 99, 116, 58, 58, 115, 101, 114, 100, 101, 58, 58, 115, 101, 114, 58, 58, 116,
|
||||
101, 115, 116, 115, 58, 58, 77, 121, 83, 116, 114, 117, 99, 116, 123, 1, 12, 0, 0, 0,
|
||||
0, 0, 0, 0, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 1, 123, 0, 0, 0,
|
||||
0, 0, 0, 0, 219, 15, 73, 64, 57, 5, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 254, 255,
|
||||
255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 254, 255, 255, 255,
|
||||
255, 255, 255, 255, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 64, 32,
|
||||
0, 0, 0, 0, 0, 0, 0, 255, 201, 154, 59, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 84, 117,
|
||||
112, 108, 101, 32, 83, 116, 114, 117, 99, 116, 0, 0, 0, 0, 1, 0, 0, 0, 123, 0, 0, 0, 0,
|
||||
0, 0, 0, 2, 0, 0, 0, 164, 112, 157, 63, 164, 112, 77, 64, 3, 0, 0, 0, 20, 0, 0, 0, 0,
|
||||
0, 0, 0, 83, 116, 114, 117, 99, 116, 32, 118, 97, 114, 105, 97, 110, 116, 32, 118, 97,
|
||||
108, 117, 101, 100, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0,
|
||||
];
|
||||
|
||||
assert_eq!(expected, bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_serialize_self_describing_binary() {
|
||||
let mut map = HashMap::new();
|
||||
map.insert(64, 32);
|
||||
|
||||
let input = MyStruct {
|
||||
primitive_value: 123,
|
||||
option_value: Some(String::from("Hello world!")),
|
||||
option_value_complex: Some(SomeStruct { foo: 123 }),
|
||||
tuple_value: (PI, 1337),
|
||||
list_value: vec![-2, -1, 0, 1, 2],
|
||||
array_value: [-2, -1, 0, 1, 2],
|
||||
map_value: map,
|
||||
struct_value: SomeStruct { foo: 999999999 },
|
||||
tuple_struct_value: SomeTupleStruct(String::from("Tuple Struct")),
|
||||
unit_enum: SomeEnum::Unit,
|
||||
newtype_enum: SomeEnum::NewType(123),
|
||||
tuple_enum: SomeEnum::Tuple(1.23, 3.21),
|
||||
struct_enum: SomeEnum::Struct {
|
||||
foo: String::from("Struct variant value"),
|
||||
},
|
||||
custom_serialize: CustomSerialize {
|
||||
value: 100,
|
||||
inner_struct: SomeSerializableStruct { foo: 101 },
|
||||
},
|
||||
};
|
||||
|
||||
let registry = get_registry();
|
||||
|
||||
let serializer = ReflectSerializer::new(&input, ®istry);
|
||||
let bytes: Vec<u8> = rmp_serde::to_vec(&serializer).unwrap();
|
||||
|
||||
let expected: Vec<u8> = vec![
|
||||
129, 217, 41, 98, 101, 118, 121, 95, 114, 101, 102, 108, 101, 99, 116, 58, 58, 115,
|
||||
101, 114, 100, 101, 58, 58, 115, 101, 114, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77,
|
||||
121, 83, 116, 114, 117, 99, 116, 158, 123, 172, 72, 101, 108, 108, 111, 32, 119, 111,
|
||||
114, 108, 100, 33, 145, 123, 146, 202, 64, 73, 15, 219, 205, 5, 57, 149, 254, 255, 0,
|
||||
1, 2, 149, 254, 255, 0, 1, 2, 129, 64, 32, 145, 206, 59, 154, 201, 255, 145, 172, 84,
|
||||
117, 112, 108, 101, 32, 83, 116, 114, 117, 99, 116, 164, 85, 110, 105, 116, 129, 167,
|
||||
78, 101, 119, 84, 121, 112, 101, 123, 129, 165, 84, 117, 112, 108, 101, 146, 202, 63,
|
||||
157, 112, 164, 202, 64, 77, 112, 164, 129, 166, 83, 116, 114, 117, 99, 116, 145, 180,
|
||||
83, 116, 114, 117, 99, 116, 32, 118, 97, 114, 105, 97, 110, 116, 32, 118, 97, 108, 117,
|
||||
101, 146, 100, 145, 101,
|
||||
];
|
||||
|
||||
assert_eq!(expected, bytes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,6 +73,7 @@ pub struct StructInfo {
|
|||
type_name: &'static str,
|
||||
type_id: TypeId,
|
||||
fields: Box<[NamedField]>,
|
||||
field_names: Box<[&'static str]>,
|
||||
field_indices: HashMap<&'static str, usize>,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Option<&'static str>,
|
||||
|
@ -93,11 +94,14 @@ impl StructInfo {
|
|||
.map(|(index, field)| (field.name(), index))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let field_names = fields.iter().map(|field| field.name()).collect();
|
||||
|
||||
Self {
|
||||
name,
|
||||
type_name: std::any::type_name::<T>(),
|
||||
type_id: TypeId::of::<T>(),
|
||||
fields: fields.to_vec().into_boxed_slice(),
|
||||
field_names,
|
||||
field_indices,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: None,
|
||||
|
@ -110,6 +114,11 @@ impl StructInfo {
|
|||
Self { docs, ..self }
|
||||
}
|
||||
|
||||
/// A slice containing the names of all fields in order.
|
||||
pub fn field_names(&self) -> &[&'static str] {
|
||||
&self.field_names
|
||||
}
|
||||
|
||||
/// Get the field with the given name.
|
||||
pub fn field(&self, name: &str) -> Option<&NamedField> {
|
||||
self.field_indices
|
||||
|
|
|
@ -26,3 +26,7 @@ ron = "0.8.0"
|
|||
uuid = { version = "1.1", features = ["v4", "serde"] }
|
||||
anyhow = "1.0.4"
|
||||
thiserror = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
postcard = { version = "1.0", features = ["alloc"] }
|
||||
bincode = "1.3"
|
||||
|
|
|
@ -273,6 +273,22 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> {
|
|||
formatter.write_str("entities")
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> std::result::Result<Self::Value, A::Error>
|
||||
where
|
||||
A: SeqAccess<'de>,
|
||||
{
|
||||
let components = seq
|
||||
.next_element_seed(ComponentDeserializer {
|
||||
registry: self.registry,
|
||||
})?
|
||||
.ok_or_else(|| Error::missing_field(ENTITY_FIELD_COMPONENTS))?;
|
||||
|
||||
Ok(DynamicEntity {
|
||||
entity: self.id,
|
||||
components,
|
||||
})
|
||||
}
|
||||
|
||||
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: MapAccess<'de>,
|
||||
|
@ -370,12 +386,13 @@ impl<'a, 'de> Visitor<'de> for ComponentVisitor<'a> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::serde::SceneDeserializer;
|
||||
use crate::DynamicSceneBuilder;
|
||||
use crate::serde::{SceneDeserializer, SceneSerializer};
|
||||
use crate::{DynamicScene, DynamicSceneBuilder};
|
||||
use bevy_app::AppTypeRegistry;
|
||||
use bevy_ecs::entity::EntityMap;
|
||||
use bevy_ecs::prelude::{Component, ReflectComponent, World};
|
||||
use bevy_reflect::Reflect;
|
||||
use bevy_reflect::{FromReflect, Reflect, ReflectSerialize};
|
||||
use bincode::Options;
|
||||
use serde::de::DeserializeSeed;
|
||||
|
||||
#[derive(Component, Reflect, Default)]
|
||||
|
@ -388,6 +405,24 @@ mod tests {
|
|||
#[reflect(Component)]
|
||||
struct Baz(i32);
|
||||
|
||||
#[derive(Component, Reflect, Default)]
|
||||
#[reflect(Component)]
|
||||
struct MyComponent {
|
||||
foo: [usize; 3],
|
||||
bar: (f32, f32),
|
||||
baz: MyEnum,
|
||||
}
|
||||
|
||||
#[derive(Reflect, FromReflect, Default)]
|
||||
enum MyEnum {
|
||||
#[default]
|
||||
Unit,
|
||||
Tuple(String),
|
||||
Struct {
|
||||
value: u32,
|
||||
},
|
||||
}
|
||||
|
||||
fn create_world() -> World {
|
||||
let mut world = World::new();
|
||||
let registry = AppTypeRegistry::default();
|
||||
|
@ -396,6 +431,12 @@ mod tests {
|
|||
registry.register::<Foo>();
|
||||
registry.register::<Bar>();
|
||||
registry.register::<Baz>();
|
||||
registry.register::<MyComponent>();
|
||||
registry.register::<MyEnum>();
|
||||
registry.register::<String>();
|
||||
registry.register_type_data::<String, ReflectSerialize>();
|
||||
registry.register::<[usize; 3]>();
|
||||
registry.register::<(f32, f32)>();
|
||||
}
|
||||
world.insert_resource(registry);
|
||||
world
|
||||
|
@ -487,4 +528,198 @@ mod tests {
|
|||
assert_eq!(2, dst_world.query::<&Bar>().iter(&dst_world).count());
|
||||
assert_eq!(1, dst_world.query::<&Baz>().iter(&dst_world).count());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_roundtrip_postcard() {
|
||||
let mut world = create_world();
|
||||
|
||||
world.spawn(MyComponent {
|
||||
foo: [1, 2, 3],
|
||||
bar: (1.3, 3.7),
|
||||
baz: MyEnum::Tuple("Hello World!".to_string()),
|
||||
});
|
||||
|
||||
let registry = world.resource::<AppTypeRegistry>();
|
||||
|
||||
let scene = DynamicScene::from_world(&world, registry);
|
||||
|
||||
let scene_serializer = SceneSerializer::new(&scene, ®istry.0);
|
||||
let serialized_scene = postcard::to_allocvec(&scene_serializer).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
vec![
|
||||
1, 0, 1, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58, 58, 115, 101, 114,
|
||||
100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111, 109, 112, 111,
|
||||
110, 101, 110, 116, 1, 2, 3, 102, 102, 166, 63, 205, 204, 108, 64, 1, 12, 72, 101,
|
||||
108, 108, 111, 32, 87, 111, 114, 108, 100, 33
|
||||
],
|
||||
serialized_scene
|
||||
);
|
||||
|
||||
let scene_deserializer = SceneDeserializer {
|
||||
type_registry: ®istry.0.read(),
|
||||
};
|
||||
let deserialized_scene = scene_deserializer
|
||||
.deserialize(&mut postcard::Deserializer::from_bytes(&serialized_scene))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(1, deserialized_scene.entities.len());
|
||||
assert_scene_eq(&scene, &deserialized_scene);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_roundtrip_bincode() {
|
||||
let mut world = create_world();
|
||||
|
||||
world.spawn(MyComponent {
|
||||
foo: [1, 2, 3],
|
||||
bar: (1.3, 3.7),
|
||||
baz: MyEnum::Tuple("Hello World!".to_string()),
|
||||
});
|
||||
|
||||
let registry = world.resource::<AppTypeRegistry>();
|
||||
|
||||
let scene = DynamicScene::from_world(&world, registry);
|
||||
|
||||
let scene_serializer = SceneSerializer::new(&scene, ®istry.0);
|
||||
let serialized_scene = bincode::serialize(&scene_serializer).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
vec![
|
||||
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0,
|
||||
0, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58, 58, 115, 101, 114, 100, 101,
|
||||
58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111, 109, 112, 111, 110, 101,
|
||||
110, 116, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0,
|
||||
102, 102, 166, 63, 205, 204, 108, 64, 1, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 72, 101,
|
||||
108, 108, 111, 32, 87, 111, 114, 108, 100, 33
|
||||
],
|
||||
serialized_scene
|
||||
);
|
||||
|
||||
let scene_deserializer = SceneDeserializer {
|
||||
type_registry: ®istry.0.read(),
|
||||
};
|
||||
|
||||
let deserialized_scene = bincode::DefaultOptions::new()
|
||||
.with_fixint_encoding()
|
||||
.deserialize_seed(scene_deserializer, &serialized_scene)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(1, deserialized_scene.entities.len());
|
||||
assert_scene_eq(&scene, &deserialized_scene);
|
||||
}
|
||||
|
||||
/// A crude equality checker for [`DynamicScene`], used solely for testing purposes.
|
||||
fn assert_scene_eq(expected: &DynamicScene, received: &DynamicScene) {
|
||||
assert_eq!(
|
||||
expected.entities.len(),
|
||||
received.entities.len(),
|
||||
"entity count did not match",
|
||||
);
|
||||
|
||||
for expected in &expected.entities {
|
||||
let received = received
|
||||
.entities
|
||||
.iter()
|
||||
.find(|dynamic_entity| dynamic_entity.entity == expected.entity)
|
||||
.unwrap_or_else(|| panic!("missing entity (expected: `{}`)", expected.entity));
|
||||
|
||||
assert_eq!(expected.entity, received.entity, "entities did not match",);
|
||||
|
||||
for expected in &expected.components {
|
||||
let received = received
|
||||
.components
|
||||
.iter()
|
||||
.find(|component| component.type_name() == expected.type_name())
|
||||
.unwrap_or_else(|| {
|
||||
panic!("missing component (expected: `{}`)", expected.type_name())
|
||||
});
|
||||
|
||||
assert!(
|
||||
expected
|
||||
.reflect_partial_eq(received.as_ref())
|
||||
.unwrap_or_default(),
|
||||
"components did not match: (expected: `{:?}`, received: `{:?}`)",
|
||||
expected,
|
||||
received
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// These tests just verify that that the [`assert_scene_eq`] function is working properly for our tests.
|
||||
mod assert_scene_eq_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "entity count did not match")]
|
||||
fn should_panic_when_entity_count_not_eq() {
|
||||
let mut world = create_world();
|
||||
let registry = world.resource::<AppTypeRegistry>();
|
||||
let scene_a = DynamicScene::from_world(&world, registry);
|
||||
|
||||
world.spawn(MyComponent {
|
||||
foo: [1, 2, 3],
|
||||
bar: (1.3, 3.7),
|
||||
baz: MyEnum::Unit,
|
||||
});
|
||||
|
||||
let registry = world.resource::<AppTypeRegistry>();
|
||||
let scene_b = DynamicScene::from_world(&world, registry);
|
||||
|
||||
assert_scene_eq(&scene_a, &scene_b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "components did not match")]
|
||||
fn should_panic_when_components_not_eq() {
|
||||
let mut world = create_world();
|
||||
|
||||
let entity = world
|
||||
.spawn(MyComponent {
|
||||
foo: [1, 2, 3],
|
||||
bar: (1.3, 3.7),
|
||||
baz: MyEnum::Unit,
|
||||
})
|
||||
.id();
|
||||
|
||||
let registry = world.resource::<AppTypeRegistry>();
|
||||
let scene_a = DynamicScene::from_world(&world, registry);
|
||||
|
||||
world.entity_mut(entity).insert(MyComponent {
|
||||
foo: [3, 2, 1],
|
||||
bar: (1.3, 3.7),
|
||||
baz: MyEnum::Unit,
|
||||
});
|
||||
|
||||
let registry = world.resource::<AppTypeRegistry>();
|
||||
let scene_b = DynamicScene::from_world(&world, registry);
|
||||
|
||||
assert_scene_eq(&scene_a, &scene_b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "missing component")]
|
||||
fn should_panic_when_missing_component() {
|
||||
let mut world = create_world();
|
||||
|
||||
let entity = world
|
||||
.spawn(MyComponent {
|
||||
foo: [1, 2, 3],
|
||||
bar: (1.3, 3.7),
|
||||
baz: MyEnum::Unit,
|
||||
})
|
||||
.id();
|
||||
|
||||
let registry = world.resource::<AppTypeRegistry>();
|
||||
let scene_a = DynamicScene::from_world(&world, registry);
|
||||
|
||||
world.entity_mut(entity).remove::<MyComponent>();
|
||||
|
||||
let registry = world.resource::<AppTypeRegistry>();
|
||||
let scene_b = DynamicScene::from_world(&world, registry);
|
||||
|
||||
assert_scene_eq(&scene_a, &scene_b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue