This is an automated email from the ASF dual-hosted git repository.
chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git
The following commit(s) were added to refs/heads/main by this push:
new 7cd451561 feat(Rust): support Option in MetaFieldType se/de (#2528)
7cd451561 is described below
commit 7cd451561ac8e79d2c8af000f71d796bc77b006c
Author: urlyy <[email protected]>
AuthorDate: Tue Aug 26 17:28:21 2025 +0800
feat(Rust): support Option in MetaFieldType se/de (#2528)
## Why?
Rust does not support objects being `null`; instead, it uses the
`Option` type to wrap nullable objects. In the current Rust macros,
`Option<T>` is extracted as `MetaFieldType { name: "Option", generics:
[T] }`, and then compared with the `MetaFieldType { name: T, generics:
[] }` received from the sender peer like java. This causes a type
mismatch. Therefore, the `Option` type requires special handling.
## What does this PR do?
- support `Option` in MetaFieldType se/de. Specifically, during Rust
peer serialization, I will serialize `Option<T>` as `(T::type_id,
nullable=true)`. During deserialization, `(T::type_id, nullable=true)`
will be converted to `Option<T>`, while `(T::type_id, nullable=false)`
will be converted to `T`.
- correct the previous mistake in #2492, rename `FieldId` to `TypeId`.
## notice
It doesn’t support handling adjacent Options, such as Option<Option<T>>.
## Related issues
#2526
## Does this PR introduce any user-facing change?
- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
## Benchmark
---
rust/fory-core/src/meta/type_meta.rs | 74 +++++++++++++++----------
rust/fory-core/src/resolver/type_resolver.rs | 22 ++++----
rust/fory-core/src/serializer/any.rs | 4 +-
rust/fory-core/src/serializer/bool.rs | 4 +-
rust/fory-core/src/serializer/datetime.rs | 6 +-
rust/fory-core/src/serializer/list.rs | 4 +-
rust/fory-core/src/serializer/map.rs | 4 +-
rust/fory-core/src/serializer/number.rs | 14 ++---
rust/fory-core/src/serializer/primitive_list.rs | 16 +++---
rust/fory-core/src/serializer/set.rs | 4 +-
rust/fory-core/src/serializer/string.rs | 4 +-
rust/fory-core/src/types.rs | 4 +-
rust/fory-derive/src/object/read.rs | 2 +-
rust/fory-derive/src/object/util.rs | 13 ++++-
rust/tests/tests/test_compatible.rs | 21 ++++---
15 files changed, 116 insertions(+), 80 deletions(-)
diff --git a/rust/fory-core/src/meta/type_meta.rs
b/rust/fory-core/src/meta/type_meta.rs
index e60af000f..eca526511 100644
--- a/rust/fory-core/src/meta/type_meta.rs
+++ b/rust/fory-core/src/meta/type_meta.rs
@@ -20,7 +20,7 @@ use crate::buffer::{Reader, Writer};
use crate::error::Error;
use crate::meta::murmurhash3_x64_128;
use crate::meta::{Encoding, MetaStringDecoder};
-use crate::types::FieldId;
+use crate::types::TypeId;
use anyhow::anyhow;
use std::cmp::min;
@@ -50,26 +50,33 @@ impl FieldType {
FieldType { type_id, generics }
}
- fn to_bytes(&self, writer: &mut Writer, write_flag: bool) -> Result<(),
Error> {
+ fn to_bytes(&self, writer: &mut Writer, write_flag: bool, nullable: bool)
-> Result<(), Error> {
+ if self.type_id == TypeId::ForyOption.into() {
+ self.generics
+ .first()
+ .unwrap()
+ .to_bytes(writer, write_flag, true)?;
+ return Ok(());
+ }
let mut header: i32 = self.type_id as i32;
- // let ref_tracking = false;
- // todo if "Option<T>" is nullability then T nullability=true
- // let nullability = false;
if write_flag {
header <<= 2;
+ // let ref_tracking = false;
+ if nullable {
+ header |= 2;
+ }
}
writer.var_int32(header);
-
match self.type_id {
- x if x == FieldId::ARRAY as i16 || x == FieldId::SET as i16 => {
+ x if x == TypeId::ARRAY as i16 || x == TypeId::SET as i16 => {
let generic = self.generics.first().unwrap();
- generic.to_bytes(writer, true)?;
+ generic.to_bytes(writer, true, false)?;
}
- x if x == FieldId::MAP as i16 => {
+ x if x == TypeId::MAP as i16 => {
let key_generic = self.generics.first().unwrap();
let val_generic = self.generics.get(1).unwrap();
- key_generic.to_bytes(writer, true)?;
- val_generic.to_bytes(writer, true)?;
+ key_generic.to_bytes(writer, true, false)?;
+ val_generic.to_bytes(writer, true, false)?;
}
_ => {}
}
@@ -77,28 +84,29 @@ impl FieldType {
}
#[allow(clippy::needless_late_init)]
- fn from_bytes(reader: &mut Reader, read_flag: bool) -> Self {
+ fn from_bytes(reader: &mut Reader, read_flag: bool, nullable:
Option<bool>) -> Self {
let header = reader.var_int32();
let type_id;
+ let _nullable;
if read_flag {
type_id = (header >> 2) as i16;
// let tracking_ref = (header & 1) != 0;
- // todo if T is nullability then "Option<T>"
- // let nullable = (header & 2) != 0;
+ _nullable = (header & 2) != 0;
} else {
type_id = header as i16;
+ _nullable = nullable.unwrap();
}
- match type_id {
- x if x == FieldId::ARRAY as i16 || x == FieldId::SET as i16 => {
- let generic = Self::from_bytes(reader, true);
+ let field_type = match type_id {
+ x if x == TypeId::ARRAY.into() || x == TypeId::SET.into() => {
+ let generic = Self::from_bytes(reader, true, None);
Self {
type_id,
generics: vec![generic],
}
}
- x if x == FieldId::MAP as i16 => {
- let key_generic = Self::from_bytes(reader, true);
- let val_generic = Self::from_bytes(reader, true);
+ x if x == TypeId::MAP.into() => {
+ let key_generic = Self::from_bytes(reader, true, None);
+ let val_generic = Self::from_bytes(reader, true, None);
Self {
type_id,
generics: vec![key_generic, val_generic],
@@ -108,6 +116,14 @@ impl FieldType {
type_id,
generics: vec![],
},
+ };
+ if _nullable {
+ Self {
+ type_id: TypeId::ForyOption.into(),
+ generics: vec![field_type],
+ }
+ } else {
+ field_type
}
}
}
@@ -139,7 +155,7 @@ impl FieldInfo {
pub fn from_bytes(reader: &mut Reader) -> FieldInfo {
let header = reader.u8();
- // let nullability = (header & 2) != 0;
+ let nullable = (header & 2) != 0;
// let ref_tracking = (header & 1) != 0;
let encoding = Self::u8_to_encoding((header >> 6) & 0b11).unwrap();
let mut name_size = ((header >> 2) & FIELD_NAME_SIZE_THRESHOLD as u8)
as usize;
@@ -148,7 +164,7 @@ impl FieldInfo {
}
name_size += 1;
- let field_type = FieldType::from_bytes(reader, false);
+ let field_type = FieldType::from_bytes(reader, false,
Option::from(nullable));
let field_name_bytes = reader.bytes(name_size);
@@ -171,12 +187,12 @@ impl FieldInfo {
let name_encoded = meta_string.bytes.as_slice();
let name_size = name_encoded.len() - 1;
let mut header: u8 = (min(FIELD_NAME_SIZE_THRESHOLD, name_size) as u8)
<< 2;
- let ref_tracking = false;
- let nullability = false;
- if ref_tracking {
- header |= 1;
- }
- if nullability {
+ // let ref_tracking = false;
+ let nullable = self.field_type.type_id == TypeId::ForyOption.into();
+ // if ref_tracking {
+ // header |= 1;
+ // }
+ if nullable {
header |= 2;
}
let encoding_idx = ENCODING_OPTIONS
@@ -188,7 +204,7 @@ impl FieldInfo {
if name_size >= FIELD_NAME_SIZE_THRESHOLD {
writer.var_int32((name_size - FIELD_NAME_SIZE_THRESHOLD) as i32);
}
- self.field_type.to_bytes(&mut writer, false)?;
+ self.field_type.to_bytes(&mut writer, false, nullable)?;
// write field_name
writer.bytes(name_encoded);
Ok(writer.dump())
diff --git a/rust/fory-core/src/resolver/type_resolver.rs
b/rust/fory-core/src/resolver/type_resolver.rs
index de8379da4..eea1f347f 100644
--- a/rust/fory-core/src/resolver/type_resolver.rs
+++ b/rust/fory-core/src/resolver/type_resolver.rs
@@ -19,7 +19,7 @@ use super::context::{ReadContext, WriteContext};
use crate::error::Error;
use crate::fory::Fory;
use crate::serializer::{Serializer, StructSerializer};
-use crate::types::FieldId;
+use crate::types::TypeId;
use chrono::{NaiveDate, NaiveDateTime};
use std::{any::Any, collections::HashMap};
@@ -100,18 +100,18 @@ impl Default for TypeResolver {
fn default() -> Self {
let mut serialize_map = HashMap::new();
- register_harness!(bool, FieldId::BOOL, serialize_map);
- register_harness!(i8, FieldId::INT8, serialize_map);
- register_harness!(i16, FieldId::INT16, serialize_map);
- register_harness!(i32, FieldId::INT32, serialize_map);
- register_harness!(i64, FieldId::INT64, serialize_map);
- register_harness!(f32, FieldId::FLOAT32, serialize_map);
- register_harness!(f64, FieldId::FLOAT64, serialize_map);
+ register_harness!(bool, TypeId::BOOL, serialize_map);
+ register_harness!(i8, TypeId::INT8, serialize_map);
+ register_harness!(i16, TypeId::INT16, serialize_map);
+ register_harness!(i32, TypeId::INT32, serialize_map);
+ register_harness!(i64, TypeId::INT64, serialize_map);
+ register_harness!(f32, TypeId::FLOAT32, serialize_map);
+ register_harness!(f64, TypeId::FLOAT64, serialize_map);
- register_harness!(String, FieldId::STRING, serialize_map);
+ register_harness!(String, TypeId::STRING, serialize_map);
- register_harness!(NaiveDate, FieldId::LOCAL_DATE, serialize_map);
- register_harness!(NaiveDateTime, FieldId::TIMESTAMP, serialize_map);
+ register_harness!(NaiveDate, TypeId::LOCAL_DATE, serialize_map);
+ register_harness!(NaiveDateTime, TypeId::TIMESTAMP, serialize_map);
TypeResolver {
serialize_map,
diff --git a/rust/fory-core/src/serializer/any.rs
b/rust/fory-core/src/serializer/any.rs
index c6cc1023a..cf9ab52ce 100644
--- a/rust/fory-core/src/serializer/any.rs
+++ b/rust/fory-core/src/serializer/any.rs
@@ -19,7 +19,7 @@ use crate::error::Error;
use crate::fory::Fory;
use crate::resolver::context::{ReadContext, WriteContext};
use crate::serializer::Serializer;
-use crate::types::{FieldId, Mode, RefFlag};
+use crate::types::{Mode, RefFlag, TypeId};
use anyhow::anyhow;
use std::any::Any;
@@ -37,7 +37,7 @@ impl Serializer for Box<dyn Any> {
}
fn get_type_id(_fory: &Fory) -> i16 {
- FieldId::ForyTypeTag.into()
+ TypeId::ForyTypeTag.into()
}
fn serialize(&self, context: &mut WriteContext) {
diff --git a/rust/fory-core/src/serializer/bool.rs
b/rust/fory-core/src/serializer/bool.rs
index 0586cea57..1fb0f4f5b 100644
--- a/rust/fory-core/src/serializer/bool.rs
+++ b/rust/fory-core/src/serializer/bool.rs
@@ -20,7 +20,7 @@ use crate::fory::Fory;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
-use crate::types::FieldId;
+use crate::types::TypeId;
use std::mem;
impl Serializer for bool {
@@ -37,6 +37,6 @@ impl Serializer for bool {
}
fn get_type_id(_fory: &Fory) -> i16 {
- FieldId::BOOL.into()
+ TypeId::BOOL.into()
}
}
diff --git a/rust/fory-core/src/serializer/datetime.rs
b/rust/fory-core/src/serializer/datetime.rs
index 5a1248804..8084ffd44 100644
--- a/rust/fory-core/src/serializer/datetime.rs
+++ b/rust/fory-core/src/serializer/datetime.rs
@@ -20,7 +20,7 @@ use crate::fory::Fory;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
-use crate::types::{FieldId, ForyGeneralList};
+use crate::types::{ForyGeneralList, TypeId};
use crate::util::EPOCH;
use anyhow::anyhow;
use chrono::{DateTime, Days, NaiveDate, NaiveDateTime};
@@ -45,7 +45,7 @@ impl Serializer for NaiveDateTime {
}
fn get_type_id(_fory: &Fory) -> i16 {
- FieldId::TIMESTAMP.into()
+ TypeId::TIMESTAMP.into()
}
}
@@ -71,7 +71,7 @@ impl Serializer for NaiveDate {
}
fn get_type_id(_fory: &Fory) -> i16 {
- FieldId::LOCAL_DATE.into()
+ TypeId::LOCAL_DATE.into()
}
}
diff --git a/rust/fory-core/src/serializer/list.rs
b/rust/fory-core/src/serializer/list.rs
index a831adc4e..43490aeb6 100644
--- a/rust/fory-core/src/serializer/list.rs
+++ b/rust/fory-core/src/serializer/list.rs
@@ -20,7 +20,7 @@ use crate::fory::Fory;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
-use crate::types::{FieldId, ForyGeneralList, SIZE_OF_REF_AND_TYPE};
+use crate::types::{ForyGeneralList, TypeId, SIZE_OF_REF_AND_TYPE};
use std::mem;
impl<T> Serializer for Vec<T>
@@ -51,7 +51,7 @@ where
}
fn get_type_id(_fory: &Fory) -> i16 {
- FieldId::ARRAY.into()
+ TypeId::ARRAY.into()
}
}
diff --git a/rust/fory-core/src/serializer/map.rs
b/rust/fory-core/src/serializer/map.rs
index 75502feeb..6f00bddde 100644
--- a/rust/fory-core/src/serializer/map.rs
+++ b/rust/fory-core/src/serializer/map.rs
@@ -20,7 +20,7 @@ use crate::fory::Fory;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
-use crate::types::{FieldId, ForyGeneralList, SIZE_OF_REF_AND_TYPE};
+use crate::types::{ForyGeneralList, TypeId, SIZE_OF_REF_AND_TYPE};
use std::collections::HashMap;
use std::mem;
@@ -57,7 +57,7 @@ impl<T1: Serializer + Eq + std::hash::Hash, T2: Serializer>
Serializer for HashM
}
fn get_type_id(_fory: &Fory) -> i16 {
- FieldId::MAP.into()
+ TypeId::MAP.into()
}
}
diff --git a/rust/fory-core/src/serializer/number.rs
b/rust/fory-core/src/serializer/number.rs
index 1f6b9d1f7..2d637e376 100644
--- a/rust/fory-core/src/serializer/number.rs
+++ b/rust/fory-core/src/serializer/number.rs
@@ -20,7 +20,7 @@ use crate::fory::Fory;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
-use crate::types::{FieldId, ForyGeneralList};
+use crate::types::{ForyGeneralList, TypeId};
macro_rules! impl_num_serializer {
($name: ident, $ty:tt, $field_type: expr) => {
@@ -48,9 +48,9 @@ impl ForyGeneralList for u16 {}
impl ForyGeneralList for u32 {}
impl ForyGeneralList for u64 {}
-impl_num_serializer!(i8, i8, FieldId::INT8);
-impl_num_serializer!(i16, i16, FieldId::INT16);
-impl_num_serializer!(i32, i32, FieldId::INT32);
-impl_num_serializer!(i64, i64, FieldId::INT64);
-impl_num_serializer!(f32, f32, FieldId::FLOAT32);
-impl_num_serializer!(f64, f64, FieldId::FLOAT64);
+impl_num_serializer!(i8, i8, TypeId::INT8);
+impl_num_serializer!(i16, i16, TypeId::INT16);
+impl_num_serializer!(i32, i32, TypeId::INT32);
+impl_num_serializer!(i64, i64, TypeId::INT64);
+impl_num_serializer!(f32, f32, TypeId::FLOAT32);
+impl_num_serializer!(f64, f64, TypeId::FLOAT64);
diff --git a/rust/fory-core/src/serializer/primitive_list.rs
b/rust/fory-core/src/serializer/primitive_list.rs
index cd198aa18..0eeb7cf51 100644
--- a/rust/fory-core/src/serializer/primitive_list.rs
+++ b/rust/fory-core/src/serializer/primitive_list.rs
@@ -20,7 +20,7 @@ use crate::fory::Fory;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
-use crate::types::FieldId;
+use crate::types::TypeId;
use std::mem;
pub fn to_u8_slice<T>(slice: &[T]) -> &[u8] {
@@ -78,7 +78,7 @@ impl Serializer for Vec<bool> {
}
fn get_type_id(_fory: &Fory) -> i16 {
- FieldId::ForyPrimitiveBoolArray.into()
+ TypeId::ForyPrimitiveBoolArray.into()
}
fn read(context: &mut ReadContext) -> Result<Self, Error> {
@@ -88,9 +88,9 @@ impl Serializer for Vec<bool> {
}
}
-impl_primitive_vec!(u8, u8, FieldId::BINARY);
-impl_primitive_vec!(i16, i16, FieldId::ForyPrimitiveShortArray);
-impl_primitive_vec!(i32, i32, FieldId::ForyPrimitiveIntArray);
-impl_primitive_vec!(i64, i64, FieldId::ForyPrimitiveLongArray);
-impl_primitive_vec!(f32, f32, FieldId::ForyPrimitiveFloatArray);
-impl_primitive_vec!(f64, f64, FieldId::ForyPrimitiveDoubleArray);
+impl_primitive_vec!(u8, u8, TypeId::BINARY);
+impl_primitive_vec!(i16, i16, TypeId::ForyPrimitiveShortArray);
+impl_primitive_vec!(i32, i32, TypeId::ForyPrimitiveIntArray);
+impl_primitive_vec!(i64, i64, TypeId::ForyPrimitiveLongArray);
+impl_primitive_vec!(f32, f32, TypeId::ForyPrimitiveFloatArray);
+impl_primitive_vec!(f64, f64, TypeId::ForyPrimitiveDoubleArray);
diff --git a/rust/fory-core/src/serializer/set.rs
b/rust/fory-core/src/serializer/set.rs
index 04af9efd5..6140e5478 100644
--- a/rust/fory-core/src/serializer/set.rs
+++ b/rust/fory-core/src/serializer/set.rs
@@ -20,7 +20,7 @@ use crate::fory::Fory;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
-use crate::types::{FieldId, ForyGeneralList, SIZE_OF_REF_AND_TYPE};
+use crate::types::{ForyGeneralList, TypeId, SIZE_OF_REF_AND_TYPE};
use std::collections::HashSet;
use std::mem;
@@ -52,7 +52,7 @@ impl<T: Serializer + Eq + std::hash::Hash> Serializer for
HashSet<T> {
}
fn get_type_id(_fory: &Fory) -> i16 {
- FieldId::SET.into()
+ TypeId::SET.into()
}
}
diff --git a/rust/fory-core/src/serializer/string.rs
b/rust/fory-core/src/serializer/string.rs
index 3b8a90f72..5c5c3d454 100644
--- a/rust/fory-core/src/serializer/string.rs
+++ b/rust/fory-core/src/serializer/string.rs
@@ -20,7 +20,7 @@ use crate::fory::Fory;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
-use crate::types::{FieldId, ForyGeneralList};
+use crate::types::{ForyGeneralList, TypeId};
use std::mem;
impl Serializer for String {
@@ -39,7 +39,7 @@ impl Serializer for String {
}
fn get_type_id(_fory: &Fory) -> i16 {
- FieldId::STRING.into()
+ TypeId::STRING.into()
}
}
diff --git a/rust/fory-core/src/types.rs b/rust/fory-core/src/types.rs
index 0f0d2f701..f8bddebdc 100644
--- a/rust/fory-core/src/types.rs
+++ b/rust/fory-core/src/types.rs
@@ -42,7 +42,7 @@ pub enum RefFlag {
#[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive, IntoPrimitive)]
#[allow(non_camel_case_types)]
#[repr(i16)]
-pub enum FieldId {
+pub enum TypeId {
BOOL = 1,
INT8 = 2,
INT16 = 3,
@@ -91,6 +91,8 @@ pub enum FieldId {
ForyPrimitiveFloatArray = 262,
ForyPrimitiveDoubleArray = 263,
ForyStringArray = 264,
+ // only used at receiver peer
+ ForyOption = 265,
}
pub trait ForyGeneralList {}
diff --git a/rust/fory-derive/src/object/read.rs
b/rust/fory-derive/src/object/read.rs
index 71be7670d..5652c11bd 100644
--- a/rust/fory-derive/src/object/read.rs
+++ b/rust/fory-derive/src/object/read.rs
@@ -112,7 +112,7 @@ fn deserialize_compatible(fields: &[&Field]) -> TokenStream
{
#(#pattern_items),*
_ => {
// skip bytes
- println!("no need to deserialize {:?}",
_field.field_name.as_str());
+ println!("no need to deserialize {:?}:{:?}",
_field.field_name.as_str(), _field.field_type);
let _ = context
.get_fory()
.get_type_resolver()
diff --git a/rust/fory-derive/src/object/util.rs
b/rust/fory-derive/src/object/util.rs
index ddae33b0f..8981ddaee 100644
--- a/rust/fory-derive/src/object/util.rs
+++ b/rust/fory-derive/src/object/util.rs
@@ -15,6 +15,7 @@
// specific language governing permissions and limitations
// under the License.
+use fory_core::types::TypeId;
use proc_macro2::TokenStream;
use quote::quote;
use std::fmt;
@@ -94,9 +95,19 @@ pub(super) fn generic_tree_to_tokens(node: &TypeNode,
have_context: bool) -> Tok
fory
}
};
+ let get_type_id = if node.name == "Option" {
+ let option_type_id: i16 = TypeId::ForyOption.into();
+ quote! {
+ #option_type_id
+ }
+ } else {
+ quote! {
+ <#ty as fory_core::serializer::Serializer>::get_type_id(#param)
+ }
+ };
quote! {
fory_core::meta::FieldType::new(
- <#ty as fory_core::serializer::Serializer>::get_type_id(#param),
+ #get_type_id,
vec![#(#children_tokens),*] as Vec<fory_core::meta::FieldType>
)
}
diff --git a/rust/tests/tests/test_compatible.rs
b/rust/tests/tests/test_compatible.rs
index 4e72f4735..f31e5d4a1 100644
--- a/rust/tests/tests/test_compatible.rs
+++ b/rust/tests/tests/test_compatible.rs
@@ -65,20 +65,27 @@ fn simple() {
#[test]
fn option() {
- #[derive(Fory, Debug)]
+ #[derive(Fory, Debug, PartialEq)]
struct Animal {
f1: Option<String>,
f2: Option<String>,
+ f3: Vec<Option<String>>,
+ // adjacent Options are not supported
+ // f4: Option<Option<String>>,
+ f5: Vec<Option<Vec<Option<String>>>>,
}
- let mut fory1 = Fory::default().mode(Compatible);
- fory1.register::<Animal>(999);
+ let mut fory = Fory::default().mode(Compatible);
+ fory.register::<Animal>(999);
let animal: Animal = Animal {
- f1: Some(String::from("foo")),
+ f1: Some(String::from("f1")),
f2: None,
+ f3: vec![Option::<String>::None, Some(String::from("f3"))],
+ // f4: Some(Some(String::from("f4"))),
+ f5: vec![Some(vec![Some(String::from("f1"))])],
};
- let _bin = fory1.serialize(&animal);
- // todo
- // let obj: crate::Animal2 = fory2.deserialize(&bin).unwrap();
+ let bin = fory.serialize(&animal);
+ let obj: Animal = fory.deserialize(&bin).unwrap();
+ assert_eq!(animal, obj);
}
// #[test]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]