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 9a032a1d6 feat(Rust): Support automatic conversion between T and
Option<T> when deserialize (#2563)
9a032a1d6 is described below
commit 9a032a1d67fcc759cc7b423fbc3bf4025489945a
Author: urlyy <[email protected]>
AuthorDate: Thu Sep 4 00:01:11 2025 +0800
feat(Rust): Support automatic conversion between T and Option<T> when
deserialize (#2563)
## Why?
Fory needs to support the following conversions when `deserialize`
(Option means nullable type)(left is sender_field_type, right is
receiver_field_type):
- value -> value
- value -> Option(value)
- Option(value) -> value
- Option(value) -> Option(value)
- Option(None) -> Option(None)
- Option(None) -> value_default
## What does this PR do?
- Support automatic conversion between `T` and `Option<T>` when
deserialize. The usage can be seen in
`test_complex_struct.rs::nullable()`.
- Add NullableFieldType to represent `T{nullable}`, while the original
FieldType exactly matches the data structure of a struct, such as
`Option{T}`. Comparison of `NullableFieldType` ignores the `nullable`
attribute.
- Add `read_var_uint32()` && `write_var_uint32()`.
- Changed the type of `type_id` from `i16` to `u32`, and updated the
read/write operations from `i16()` to `var_uint32()`
- When registering a struct, add `types::STRUCT` to its id in addition
to the original type_id
- Removed the sending of `meta_index`.
- For reuse, extract the `read_compatible` function.
## Detail
1. The processing approach is as follows:
- At compile time, parse and obtain the `FieldType` instance. The
`FieldType` (e.g., `Option{T}`) can be converted into a
`NullableFieldType` (e.g., `T{nullable=true}`).
- At compile time, based on the `NullableFieldType`, generate a
`_deserialize_nullable_{field_name}` function for each field. This
function defines the read process constrained by the
`local_nullable_field_type` (compile-time instance, obtained through
macro parsing) and determines the actual data read according to the
`remote_nullable_field_type` (runtime instance, obtained from the
received type_meta).
- Deserialization flow:
```rust
if local_field_name_f1 == remote_field_name {
if local_field_type_f1 == remote_field_type {
f1 = <f1_T>::deserialize();
} else {
if nullable_local_field_type_f1 == nullable_remote_field_type {
f1 = Self::_deserialize_nullable_f1(remote_nullable_field_type);
} else {
skip_field_value(remote_field_type);
f1 = default();
}
}
} else if local_field_name_f2 == remote_field_name {
...
} else if ... {
...
} else {
skip_field_value(remote_field_type);
}
```
2. Adjacent `Option`s like `Option<Option<T>>` are not supported. If
such a field is defined, a compile-time error will be raised: `adjacent
Options are not supported`.
## Related issues
#2529
## Does this PR introduce any user-facing change?
- [ ] Does this PR introduce any public API change?
- [x] Does this PR introduce any binary protocol compatibility change?
## Benchmark
---
rust/fory-core/src/buffer.rs | 56 +++++
rust/fory-core/src/fory.rs | 7 +-
rust/fory-core/src/meta/mod.rs | 2 +-
rust/fory-core/src/meta/type_meta.rs | 76 ++++--
rust/fory-core/src/resolver/context.rs | 10 +
rust/fory-core/src/resolver/type_resolver.rs | 14 +-
rust/fory-core/src/serializer/any.rs | 77 +++---
rust/fory-core/src/serializer/bool.rs | 4 +-
rust/fory-core/src/serializer/datetime.rs | 8 +-
rust/fory-core/src/serializer/list.rs | 6 +-
rust/fory-core/src/serializer/map.rs | 4 +-
rust/fory-core/src/serializer/mod.rs | 17 +-
rust/fory-core/src/serializer/nonexistent.rs | 108 ---------
rust/fory-core/src/serializer/number.rs | 4 +-
rust/fory-core/src/serializer/option.rs | 9 +-
rust/fory-core/src/serializer/primitive_list.rs | 8 +-
rust/fory-core/src/serializer/set.rs | 6 +-
rust/fory-core/src/serializer/skip.rs | 106 ++++++++
rust/fory-core/src/serializer/string.rs | 4 +-
rust/fory-core/src/types.rs | 15 ++
rust/fory-derive/src/object/derive_enum.rs | 2 +-
rust/fory-derive/src/object/misc.rs | 8 +-
rust/fory-derive/src/object/read.rs | 123 +++++++---
rust/fory-derive/src/object/serializer.rs | 18 +-
rust/fory-derive/src/object/util.rs | 306 +++++++++++++++++++++++-
rust/fory-derive/src/object/write.rs | 9 +-
rust/tests/tests/test_buffer.rs | 8 +
rust/tests/tests/test_compatible.rs | 250 ++++++++++++++++++-
rust/tests/tests/test_complex_struct.rs | 2 +-
29 files changed, 1005 insertions(+), 262 deletions(-)
diff --git a/rust/fory-core/src/buffer.rs b/rust/fory-core/src/buffer.rs
index 2cc6b2744..9e1715745 100644
--- a/rust/fory-core/src/buffer.rs
+++ b/rust/fory-core/src/buffer.rs
@@ -115,6 +115,40 @@ impl Writer {
}
}
+ pub fn var_uint32(&mut self, value: u32) {
+ if value >> 7 == 0 {
+ self.u8(value as u8);
+ } else if value >> 14 == 0 {
+ let u1 = (value & 0x7F) | 0x80;
+ let u2 = value >> 7;
+ self.u16(((u2 << 8) | u1) as u16);
+ } else if value >> 21 == 0 {
+ let u1 = (value & 0x7F) | 0x80;
+ let u2 = ((value >> 7) & 0x7F) | 0x80;
+ let u3 = value >> 14;
+ self.u8(u1 as u8);
+ self.u8(u2 as u8);
+ self.u8(u3 as u8);
+ } else if value >> 28 == 0 {
+ let u1 = (value & 0x7F) | 0x80;
+ let u2 = ((value >> 7) & 0x7F) | 0x80;
+ let u3 = ((value >> 14) & 0x7F) | 0x80;
+ let u4 = value >> 21;
+ self.u32((u4 << 24) | (u3 << 16) | (u2 << 8) | u1);
+ } else {
+ let u1 = (value & 0x7F) | 0x80;
+ let u2 = ((value >> 7) & 0x7F) | 0x80;
+ let u3 = ((value >> 14) & 0x7F) | 0x80;
+ let u4 = ((value >> 21) & 0x7F) | 0x80;
+ let u5 = value >> 28;
+ self.u8(u1 as u8);
+ self.u8(u2 as u8);
+ self.u8(u3 as u8);
+ self.u8(u4 as u8);
+ self.u8(u5 as u8);
+ }
+ }
+
pub fn bytes(&mut self, v: &[u8]) {
self.reserve(v.len());
self.bf.extend_from_slice(v);
@@ -228,6 +262,28 @@ impl<'bf> Reader<'bf> {
result
}
+ pub fn var_uint32(&mut self) -> u32 {
+ let mut byte_ = self.i8() as u32;
+ let mut result = byte_ & 0x7F;
+ if (byte_ & 0x80) != 0 {
+ byte_ = self.i8() as u32;
+ result |= (byte_ & 0x7F) << 7;
+ if (byte_ & 0x80) != 0 {
+ byte_ = self.i8() as u32;
+ result |= (byte_ & 0x7F) << 14;
+ if (byte_ & 0x80) != 0 {
+ byte_ = self.i8() as u32;
+ result |= (byte_ & 0x7F) << 21;
+ if (byte_ & 0x80) != 0 {
+ byte_ = self.i8() as u32;
+ result |= (byte_ & 0x7F) << 28;
+ }
+ }
+ }
+ }
+ result
+ }
+
pub fn string(&mut self, len: usize) -> String {
let result = String::from_utf8_lossy(&self.bf[self.cursor..self.cursor
+ len]).to_string();
self.move_next(len);
diff --git a/rust/fory-core/src/fory.rs b/rust/fory-core/src/fory.rs
index 42c4492e1..ed83f6418 100644
--- a/rust/fory-core/src/fory.rs
+++ b/rust/fory-core/src/fory.rs
@@ -90,8 +90,9 @@ impl Fory {
&self.type_resolver
}
- pub fn register<T: 'static + StructSerializer>(&mut self, id: i16) {
- let type_info = TypeInfo::new::<T>(self, id);
- self.type_resolver.register::<T>(type_info, id);
+ pub fn register<T: 'static + StructSerializer>(&mut self, id: u32) {
+ let actual_type_id = T::actual_type_id(id);
+ let type_info = TypeInfo::new::<T>(self, actual_type_id);
+ self.type_resolver.register::<T>(type_info, actual_type_id);
}
}
diff --git a/rust/fory-core/src/meta/mod.rs b/rust/fory-core/src/meta/mod.rs
index 04f51a985..27f59ab07 100644
--- a/rust/fory-core/src/meta/mod.rs
+++ b/rust/fory-core/src/meta/mod.rs
@@ -21,4 +21,4 @@ mod type_meta;
pub use meta_string::{Encoding, MetaString, MetaStringDecoder,
MetaStringEncoder};
pub use string_util::murmurhash3_x64_128;
-pub use type_meta::{FieldInfo, FieldType, TypeMeta, TypeMetaLayer};
+pub use type_meta::{FieldInfo, FieldType, NullableFieldType, TypeMeta,
TypeMetaLayer};
diff --git a/rust/fory-core/src/meta/type_meta.rs
b/rust/fory-core/src/meta/type_meta.rs
index a436f2e89..bfd4a5e2f 100644
--- a/rust/fory-core/src/meta/type_meta.rs
+++ b/rust/fory-core/src/meta/type_meta.rs
@@ -41,17 +41,56 @@ static ENCODING_OPTIONS: &[Encoding] = &[
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct FieldType {
- pub type_id: i16,
+ pub type_id: u32,
pub generics: Vec<FieldType>,
}
+#[derive(Debug, Clone)]
+pub struct NullableFieldType {
+ pub type_id: u32,
+ pub generics: Vec<NullableFieldType>,
+ pub nullable: bool,
+}
+
+impl NullableFieldType {
+ pub fn from(node: FieldType) -> Self {
+ if node.type_id == TypeId::ForyOption as u32 {
+ let inner =
NullableFieldType::from(node.generics.into_iter().next().unwrap());
+ NullableFieldType {
+ type_id: inner.type_id,
+ generics: inner.generics,
+ nullable: true,
+ }
+ } else {
+ let generics = node
+ .generics
+ .into_iter()
+ .map(NullableFieldType::from)
+ .collect();
+ NullableFieldType {
+ type_id: node.type_id,
+ generics,
+ nullable: false,
+ }
+ }
+ }
+}
+
+impl PartialEq for NullableFieldType {
+ fn eq(&self, other: &Self) -> bool {
+ self.type_id == other.type_id && self.generics == other.generics
+ }
+}
+
+impl Eq for NullableFieldType {}
+
impl FieldType {
- pub fn new(type_id: i16, generics: Vec<FieldType>) -> Self {
+ pub fn new(type_id: u32, generics: Vec<FieldType>) -> Self {
FieldType { type_id, generics }
}
fn to_bytes(&self, writer: &mut Writer, write_flag: bool, nullable: bool)
-> Result<(), Error> {
- if self.type_id == TypeId::ForyOption.into() {
+ if self.type_id == TypeId::ForyOption as u32 {
self.generics
.first()
.unwrap()
@@ -68,11 +107,11 @@ impl FieldType {
}
writer.var_int32(header);
match self.type_id {
- x if x == TypeId::ARRAY as i16 || x == TypeId::SET as i16 => {
+ x if x == TypeId::ARRAY as u32 || x == TypeId::SET as u32 => {
let generic = self.generics.first().unwrap();
generic.to_bytes(writer, true, false)?;
}
- x if x == TypeId::MAP as i16 => {
+ x if x == TypeId::MAP as u32 => {
let key_generic = self.generics.first().unwrap();
let val_generic = self.generics.get(1).unwrap();
key_generic.to_bytes(writer, true, false)?;
@@ -83,28 +122,27 @@ impl FieldType {
Ok(())
}
- #[allow(clippy::needless_late_init)]
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;
+ type_id = (header >> 2) as u32;
// let tracking_ref = (header & 1) != 0;
_nullable = (header & 2) != 0;
} else {
- type_id = header as i16;
+ type_id = header as u32;
_nullable = nullable.unwrap();
}
let field_type = match type_id {
- x if x == TypeId::ARRAY.into() || x == TypeId::SET.into() => {
+ x if x == TypeId::ARRAY as u32 || x == TypeId::SET as u32 => {
let generic = Self::from_bytes(reader, true, None);
Self {
type_id,
generics: vec![generic],
}
}
- x if x == TypeId::MAP.into() => {
+ x if x == TypeId::MAP as u32 => {
let key_generic = Self::from_bytes(reader, true, None);
let val_generic = Self::from_bytes(reader, true, None);
Self {
@@ -119,7 +157,7 @@ impl FieldType {
};
if _nullable {
Self {
- type_id: TypeId::ForyOption.into(),
+ type_id: TypeId::ForyOption as u32,
generics: vec![field_type],
}
} else {
@@ -188,7 +226,7 @@ impl FieldInfo {
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 nullable = self.field_type.type_id == TypeId::ForyOption.into();
+ let nullable = self.field_type.type_id == TypeId::ForyOption as u32;
// if ref_tracking {
// header |= 1;
// }
@@ -213,19 +251,19 @@ impl FieldInfo {
#[derive(Debug)]
pub struct TypeMetaLayer {
- type_id: i16,
+ type_id: u32,
field_infos: Vec<FieldInfo>,
}
impl TypeMetaLayer {
- pub fn new(type_id: i16, field_infos: Vec<FieldInfo>) -> TypeMetaLayer {
+ pub fn new(type_id: u32, field_infos: Vec<FieldInfo>) -> TypeMetaLayer {
TypeMetaLayer {
type_id,
field_infos,
}
}
- pub fn get_type_id(&self) -> i16 {
+ pub fn get_type_id(&self) -> u32 {
self.type_id
}
@@ -250,7 +288,7 @@ impl TypeMetaLayer {
if is_register_by_name {
todo!()
} else {
- writer.var_int32(self.type_id as i32);
+ writer.var_uint32(self.type_id);
}
for field in self.field_infos.iter() {
writer.bytes(field.to_bytes()?.as_slice());
@@ -271,7 +309,7 @@ impl TypeMetaLayer {
if is_register_by_name {
todo!()
} else {
- type_id = reader.var_int32() as i16;
+ type_id = reader.var_uint32();
}
let mut field_infos = Vec::with_capacity(num_fields);
for _ in 0..num_fields {
@@ -293,11 +331,11 @@ impl TypeMeta {
self.layers.first().unwrap().get_field_infos()
}
- pub fn get_type_id(&self) -> i16 {
+ pub fn get_type_id(&self) -> u32 {
self.layers.first().unwrap().get_type_id()
}
- pub fn from_fields(type_id: i16, field_infos: Vec<FieldInfo>) -> TypeMeta {
+ pub fn from_fields(type_id: u32, field_infos: Vec<FieldInfo>) -> TypeMeta {
TypeMeta {
// hash: 0,
layers: vec![TypeMetaLayer::new(type_id, field_infos)],
diff --git a/rust/fory-core/src/resolver/context.rs
b/rust/fory-core/src/resolver/context.rs
index 54d8fe809..4100e3706 100644
--- a/rust/fory-core/src/resolver/context.rs
+++ b/rust/fory-core/src/resolver/context.rs
@@ -100,6 +100,16 @@ impl<'de, 'bf: 'de> ReadContext<'de, 'bf> {
self.meta_resolver.get(type_index)
}
+ pub fn get_meta_by_type_id(&self, type_id: u32) -> Rc<TypeMeta> {
+ let type_defs: Vec<_> = self.meta_resolver.reading_type_defs.to_vec();
+ for type_def in type_defs.iter() {
+ if type_def.get_type_id() == type_id {
+ return type_def.clone();
+ }
+ }
+ unreachable!()
+ }
+
pub fn load_meta(&mut self, offset: usize) {
self.meta_resolver
.load(&mut Reader::new(&self.reader.slice()[offset..]))
diff --git a/rust/fory-core/src/resolver/type_resolver.rs
b/rust/fory-core/src/resolver/type_resolver.rs
index 41fcdc557..a4cc0bb49 100644
--- a/rust/fory-core/src/resolver/type_resolver.rs
+++ b/rust/fory-core/src/resolver/type_resolver.rs
@@ -48,18 +48,18 @@ impl Harness {
pub struct TypeInfo {
type_def: Vec<u8>,
- type_id: i16,
+ type_id: u32,
}
impl TypeInfo {
- pub fn new<T: StructSerializer>(fory: &Fory, type_id: i16) -> TypeInfo {
+ pub fn new<T: StructSerializer>(fory: &Fory, type_id: u32) -> TypeInfo {
TypeInfo {
type_def: T::type_def(fory, type_id),
type_id,
}
}
- pub fn get_type_id(&self) -> i16 {
+ pub fn get_type_id(&self) -> u32 {
self.type_id
}
@@ -70,8 +70,8 @@ impl TypeInfo {
#[derive(Default)]
pub struct TypeResolver {
- serialize_map: HashMap<i16, Harness>,
- type_id_map: HashMap<std::any::TypeId, i16>,
+ serialize_map: HashMap<u32, Harness>,
+ type_id_map: HashMap<std::any::TypeId, u32>,
type_info_map: HashMap<std::any::TypeId, TypeInfo>,
}
@@ -85,7 +85,7 @@ impl TypeResolver {
})
}
- pub fn register<T: StructSerializer>(&mut self, type_info: TypeInfo, id:
i16) {
+ pub fn register<T: StructSerializer>(&mut self, type_info: TypeInfo, id:
u32) {
fn serializer<T2: 'static + StructSerializer>(this: &dyn Any, context:
&mut WriteContext) {
let this = this.downcast_ref::<T2>();
match this {
@@ -115,7 +115,7 @@ impl TypeResolver {
self.get_harness(*self.type_id_map.get(&type_id).unwrap())
}
- pub fn get_harness(&self, id: i16) -> Option<&Harness> {
+ pub fn get_harness(&self, id: u32) -> Option<&Harness> {
self.serialize_map.get(&id)
}
}
diff --git a/rust/fory-core/src/serializer/any.rs
b/rust/fory-core/src/serializer/any.rs
index 321f75fc2..9232163a0 100644
--- a/rust/fory-core/src/serializer/any.rs
+++ b/rust/fory-core/src/serializer/any.rs
@@ -19,8 +19,7 @@ use crate::error::Error;
use crate::fory::Fory;
use crate::resolver::context::{ReadContext, WriteContext};
use crate::serializer::Serializer;
-use crate::types::{Mode, RefFlag, TypeId};
-use anyhow::anyhow;
+use crate::types::TypeId;
use std::any::Any;
impl Serializer for Box<dyn Any> {
@@ -36,8 +35,8 @@ impl Serializer for Box<dyn Any> {
panic!("unreachable")
}
- fn get_type_id(_fory: &Fory) -> i16 {
- TypeId::ForyTypeTag.into()
+ fn get_type_id(_fory: &Fory) -> u32 {
+ TypeId::ForyTypeTag as u32
}
fn serialize(&self, context: &mut WriteContext) {
@@ -49,40 +48,40 @@ impl Serializer for Box<dyn Any> {
.get_serializer()(self.as_ref(), context);
}
- fn deserialize(context: &mut ReadContext) -> Result<Self, Error> {
- let reset_cursor = context.reader.reset_cursor_to_here();
- // ref flag
- let ref_flag = context.reader.i8();
-
- if ref_flag == (RefFlag::NotNullValue as i8) || ref_flag ==
(RefFlag::RefValue as i8) {
- if context.get_fory().get_mode().eq(&Mode::Compatible) {
- let meta_index = context.reader.i16();
- let type_id = context.meta_resolver.get(meta_index as
usize).get_type_id();
- reset_cursor(&mut context.reader);
- context
- .get_fory()
- .get_type_resolver()
- .get_harness(type_id)
- .unwrap()
- .get_deserializer()(context)
- } else {
- let type_id = context.reader.i16();
- reset_cursor(&mut context.reader);
- context
- .get_fory()
- .get_type_resolver()
- .get_harness(type_id)
- .unwrap()
- .get_deserializer()(context)
- }
- } else if ref_flag == (RefFlag::Null as i8) {
- Err(anyhow!("Try to deserialize `any` to null"))?
- } else if ref_flag == (RefFlag::Ref as i8) {
- reset_cursor(&mut context.reader);
- Err(Error::Ref)
- } else {
- reset_cursor(&mut context.reader);
- Err(anyhow!("Unknown ref flag, value:{ref_flag}"))?
- }
+ fn deserialize(_context: &mut ReadContext) -> Result<Self, Error> {
+ todo!()
+ // let reset_cursor = context.reader.reset_cursor_to_here();
+ // // ref flag
+ // let ref_flag = context.reader.i8();
+ //
+ // if ref_flag == (RefFlag::NotNullValue as i8) || ref_flag ==
(RefFlag::RefValue as i8) {
+ // if context.get_fory().get_mode().eq(&Mode::Compatible) {
+ // let type_id = context.meta_resolver.get(meta_index as
usize).get_type_id();
+ // reset_cursor(&mut context.reader);
+ // context
+ // .get_fory()
+ // .get_type_resolver()
+ // .get_harness(type_id)
+ // .unwrap()
+ // .get_deserializer()(context)
+ // } else {
+ // let type_id = context.reader.i16();
+ // reset_cursor(&mut context.reader);
+ // context
+ // .get_fory()
+ // .get_type_resolver()
+ // .get_harness(type_id)
+ // .unwrap()
+ // .get_deserializer()(context)
+ // }
+ // } else if ref_flag == (RefFlag::Null as i8) {
+ // Err(anyhow!("Try to deserialize `any` to null"))?
+ // } else if ref_flag == (RefFlag::Ref as i8) {
+ // reset_cursor(&mut context.reader);
+ // Err(Error::Ref)
+ // } else {
+ // reset_cursor(&mut context.reader);
+ // Err(anyhow!("Unknown ref flag, value:{ref_flag}"))?
+ // }
}
}
diff --git a/rust/fory-core/src/serializer/bool.rs
b/rust/fory-core/src/serializer/bool.rs
index 1fb0f4f5b..06b1d6a0d 100644
--- a/rust/fory-core/src/serializer/bool.rs
+++ b/rust/fory-core/src/serializer/bool.rs
@@ -36,7 +36,7 @@ impl Serializer for bool {
Ok(context.reader.u8() == 1)
}
- fn get_type_id(_fory: &Fory) -> i16 {
- TypeId::BOOL.into()
+ fn get_type_id(_fory: &Fory) -> u32 {
+ TypeId::BOOL as u32
}
}
diff --git a/rust/fory-core/src/serializer/datetime.rs
b/rust/fory-core/src/serializer/datetime.rs
index 8084ffd44..0bedf663c 100644
--- a/rust/fory-core/src/serializer/datetime.rs
+++ b/rust/fory-core/src/serializer/datetime.rs
@@ -44,8 +44,8 @@ impl Serializer for NaiveDateTime {
mem::size_of::<u64>()
}
- fn get_type_id(_fory: &Fory) -> i16 {
- TypeId::TIMESTAMP.into()
+ fn get_type_id(_fory: &Fory) -> u32 {
+ TypeId::TIMESTAMP as u32
}
}
@@ -70,8 +70,8 @@ impl Serializer for NaiveDate {
)))
}
- fn get_type_id(_fory: &Fory) -> i16 {
- TypeId::LOCAL_DATE.into()
+ fn get_type_id(_fory: &Fory) -> u32 {
+ TypeId::LOCAL_DATE as u32
}
}
diff --git a/rust/fory-core/src/serializer/list.rs
b/rust/fory-core/src/serializer/list.rs
index 43490aeb6..014e511d5 100644
--- a/rust/fory-core/src/serializer/list.rs
+++ b/rust/fory-core/src/serializer/list.rs
@@ -31,7 +31,7 @@ where
context.writer.var_int32(self.len() as i32);
context
.writer
- .reserve((<Self as Serializer>::reserved_space() +
SIZE_OF_REF_AND_TYPE) * self.len());
+ .reserve((<T as Serializer>::reserved_space() +
SIZE_OF_REF_AND_TYPE) * self.len());
for item in self.iter() {
item.serialize(context);
}
@@ -50,8 +50,8 @@ where
mem::size_of::<u32>()
}
- fn get_type_id(_fory: &Fory) -> i16 {
- TypeId::ARRAY.into()
+ fn get_type_id(_fory: &Fory) -> u32 {
+ TypeId::ARRAY as u32
}
}
diff --git a/rust/fory-core/src/serializer/map.rs
b/rust/fory-core/src/serializer/map.rs
index 6f00bddde..a829f5083 100644
--- a/rust/fory-core/src/serializer/map.rs
+++ b/rust/fory-core/src/serializer/map.rs
@@ -56,8 +56,8 @@ impl<T1: Serializer + Eq + std::hash::Hash, T2: Serializer>
Serializer for HashM
mem::size_of::<i32>()
}
- fn get_type_id(_fory: &Fory) -> i16 {
- TypeId::MAP.into()
+ fn get_type_id(_fory: &Fory) -> u32 {
+ TypeId::MAP as u32
}
}
diff --git a/rust/fory-core/src/serializer/mod.rs
b/rust/fory-core/src/serializer/mod.rs
index 10a84175f..bd07735db 100644
--- a/rust/fory-core/src/serializer/mod.rs
+++ b/rust/fory-core/src/serializer/mod.rs
@@ -20,6 +20,7 @@ use crate::error::Error;
use crate::fory::Fory;
use crate::resolver::context::{ReadContext, WriteContext};
use crate::types::RefFlag;
+use crate::types::TypeId;
use anyhow::anyhow;
mod any;
@@ -27,18 +28,20 @@ mod bool;
mod datetime;
mod list;
mod map;
-pub mod nonexistent;
mod number;
mod option;
mod primitive_list;
mod set;
+pub mod skip;
mod string;
pub fn serialize<T: Serializer>(this: &T, context: &mut WriteContext) {
// ref flag
context.writer.i8(RefFlag::NotNullValue as i8);
// type
- context.writer.i16(T::get_type_id(context.get_fory()));
+ context
+ .writer
+ .var_uint32(T::get_type_id(context.get_fory()));
this.write(context);
}
@@ -47,7 +50,7 @@ pub fn deserialize<T: Serializer>(context: &mut ReadContext)
-> Result<T, Error>
let ref_flag = context.reader.i8();
if ref_flag == (RefFlag::NotNullValue as i8) || ref_flag ==
(RefFlag::RefValue as i8) {
- let actual_type_id = context.reader.i16();
+ let actual_type_id = context.reader.var_uint32();
let expected_type_id = T::get_type_id(context.get_fory());
ensure!(
actual_type_id == expected_type_id,
@@ -89,9 +92,13 @@ where
deserialize(context)
}
- fn get_type_id(_fory: &Fory) -> i16;
+ fn get_type_id(_fory: &Fory) -> u32;
}
pub trait StructSerializer: Serializer + 'static {
- fn type_def(fory: &Fory, type_id: i16) -> Vec<u8>;
+ fn type_def(fory: &Fory, type_id: u32) -> Vec<u8>;
+
+ fn actual_type_id(type_id: u32) -> u32 {
+ (type_id << 8) + TypeId::STRUCT as u32
+ }
}
diff --git a/rust/fory-core/src/serializer/nonexistent.rs
b/rust/fory-core/src/serializer/nonexistent.rs
deleted file mode 100644
index b9732ea64..000000000
--- a/rust/fory-core/src/serializer/nonexistent.rs
+++ /dev/null
@@ -1,108 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements. See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership. The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License. You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied. See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-use crate::ensure;
-use crate::error::Error;
-use crate::meta::FieldType;
-use crate::resolver::context::ReadContext;
-use crate::serializer::Serializer;
-use crate::types::{RefFlag, TypeId, BASIC_TYPES, COLLECTION_TYPES};
-use anyhow::anyhow;
-use chrono::{NaiveDate, NaiveDateTime};
-
-macro_rules! basic_type_deserialize {
- ($tid:expr, $context:expr; $(($ty:ty, $id:ident)),+ $(,)?) => {
- $(
- if $tid == TypeId::$id {
- <$ty>::deserialize($context)?;
- return Ok(());
- }
- )+else {
- unreachable!()
- }
- };
-}
-
-pub fn skip_field_value(context: &mut ReadContext, field_type: &FieldType) ->
Result<(), Error> {
- match TypeId::try_from(field_type.type_id) {
- Ok(type_id) => {
- if BASIC_TYPES.contains(&type_id) {
- basic_type_deserialize!(type_id, context;
- (bool, BOOL),
- (i8, INT8),
- (i16, INT16),
- (i32, INT32),
- (i64, INT64),
- (f32, FLOAT32),
- (f64, FLOAT64),
- (String, STRING),
- (NaiveDate, LOCAL_DATE),
- (NaiveDateTime, TIMESTAMP),
- );
- } else if COLLECTION_TYPES.contains(&type_id) {
- let ref_flag = context.reader.i8();
- let actual_type_id = context.reader.i16();
- let type_id_num = type_id.into();
- ensure!(
- actual_type_id == type_id_num,
- anyhow!("Invalid field type, expected:{type_id_num},
actual:{actual_type_id}")
- );
- if ref_flag == (RefFlag::NotNullValue as i8)
- || ref_flag == (RefFlag::RefValue as i8)
- {
- if type_id == TypeId::ARRAY || type_id == TypeId::SET {
- let length = context.reader.var_int32() as usize;
- println!("skipping array with length {}", length);
- for _ in 0..length {
- skip_field_value(context,
field_type.generics.first().unwrap())?;
- }
- } else if type_id == TypeId::MAP {
- let length = context.reader.var_int32() as usize;
- for _ in 0..length {
- skip_field_value(context,
field_type.generics.first().unwrap())?;
- skip_field_value(context,
field_type.generics.get(1).unwrap())?;
- }
- }
- Ok(())
- } else if ref_flag == (RefFlag::Null as i8) {
- Err(anyhow!("Try to deserialize non-option type to null"))?
- } else if ref_flag == (RefFlag::Ref as i8) {
- Err(Error::Ref)
- } else {
- Err(anyhow!("Unknown ref flag, value:{ref_flag}"))?
- }
- } else {
- unreachable!()
- }
- }
- Err(_) => {
- // skip ref_flag and meta_index
- context.reader.i8();
- context.reader.i16();
- let type_defs: Vec<_> =
context.meta_resolver.reading_type_defs.to_vec();
- for type_def in type_defs.iter() {
- if type_def.get_type_id() == field_type.type_id {
- let field_infos: Vec<_> =
type_def.get_field_infos().to_vec();
- for field_info in field_infos.iter() {
- skip_field_value(context, &field_info.field_type)?;
- }
- }
- }
- Ok(())
- }
- }
-}
diff --git a/rust/fory-core/src/serializer/number.rs
b/rust/fory-core/src/serializer/number.rs
index 2d637e376..18fdadb71 100644
--- a/rust/fory-core/src/serializer/number.rs
+++ b/rust/fory-core/src/serializer/number.rs
@@ -37,8 +37,8 @@ macro_rules! impl_num_serializer {
std::mem::size_of::<$ty>()
}
- fn get_type_id(_fory: &Fory) -> i16 {
- ($field_type).into()
+ fn get_type_id(_fory: &Fory) -> u32 {
+ ($field_type) as u32
}
}
};
diff --git a/rust/fory-core/src/serializer/option.rs
b/rust/fory-core/src/serializer/option.rs
index 9b8d17bc5..f6386b82e 100644
--- a/rust/fory-core/src/serializer/option.rs
+++ b/rust/fory-core/src/serializer/option.rs
@@ -35,7 +35,7 @@ impl<T: Serializer> Serializer for Option<T> {
if ref_flag == (RefFlag::NotNullValue as i8) || ref_flag ==
(RefFlag::RefValue as i8) {
// type_id
- let actual_type_id = context.reader.i16();
+ let actual_type_id = context.reader.var_uint32();
let expected_type_id = T::get_type_id(context.get_fory());
ensure!(
actual_type_id == expected_type_id,
@@ -66,8 +66,9 @@ impl<T: Serializer> Serializer for Option<T> {
// ref flag
context.writer.i8(RefFlag::NotNullValue as i8);
// type
- context.writer.i16(T::get_type_id(context.get_fory()));
-
+ context
+ .writer
+ .var_uint32(T::get_type_id(context.get_fory()));
v.write(context);
}
None => {
@@ -80,7 +81,7 @@ impl<T: Serializer> Serializer for Option<T> {
std::mem::size_of::<T>()
}
- fn get_type_id(fory: &Fory) -> i16 {
+ fn get_type_id(fory: &Fory) -> u32 {
T::get_type_id(fory)
}
}
diff --git a/rust/fory-core/src/serializer/primitive_list.rs
b/rust/fory-core/src/serializer/primitive_list.rs
index 0eeb7cf51..cce829352 100644
--- a/rust/fory-core/src/serializer/primitive_list.rs
+++ b/rust/fory-core/src/serializer/primitive_list.rs
@@ -60,8 +60,8 @@ macro_rules! impl_primitive_vec {
mem::size_of::<i32>()
}
- fn get_type_id(_fory: &Fory) -> i16 {
- ($field_type).into()
+ fn get_type_id(_fory: &Fory) -> u32 {
+ ($field_type) as u32
}
}
};
@@ -77,8 +77,8 @@ impl Serializer for Vec<bool> {
mem::size_of::<u8>()
}
- fn get_type_id(_fory: &Fory) -> i16 {
- TypeId::ForyPrimitiveBoolArray.into()
+ fn get_type_id(_fory: &Fory) -> u32 {
+ TypeId::ForyPrimitiveBoolArray as u32
}
fn read(context: &mut ReadContext) -> Result<Self, Error> {
diff --git a/rust/fory-core/src/serializer/set.rs
b/rust/fory-core/src/serializer/set.rs
index 6140e5478..6df2e6bd9 100644
--- a/rust/fory-core/src/serializer/set.rs
+++ b/rust/fory-core/src/serializer/set.rs
@@ -27,7 +27,7 @@ use std::mem;
impl<T: Serializer + Eq + std::hash::Hash> Serializer for HashSet<T> {
fn write(&self, context: &mut WriteContext) {
// length
- context.writer.i32(self.len() as i32);
+ context.writer.var_int32(self.len() as i32);
let reserved_space =
(<T as Serializer>::reserved_space() + SIZE_OF_REF_AND_TYPE) *
self.len();
@@ -51,8 +51,8 @@ impl<T: Serializer + Eq + std::hash::Hash> Serializer for
HashSet<T> {
mem::size_of::<i32>()
}
- fn get_type_id(_fory: &Fory) -> i16 {
- TypeId::SET.into()
+ fn get_type_id(_fory: &Fory) -> u32 {
+ TypeId::SET as u32
}
}
diff --git a/rust/fory-core/src/serializer/skip.rs
b/rust/fory-core/src/serializer/skip.rs
new file mode 100644
index 000000000..54276c29f
--- /dev/null
+++ b/rust/fory-core/src/serializer/skip.rs
@@ -0,0 +1,106 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use crate::ensure;
+use crate::error::Error;
+use crate::meta::NullableFieldType;
+use crate::resolver::context::ReadContext;
+use crate::serializer::Serializer;
+use crate::types::{RefFlag, TypeId, BASIC_TYPES, COLLECTION_TYPES};
+use anyhow::anyhow;
+use chrono::{NaiveDate, NaiveDateTime};
+
+macro_rules! basic_type_deserialize {
+ ($tid:expr, $context:expr; $(($ty:ty, $id:ident)),+ $(,)?) => {
+ $(
+ if $tid == TypeId::$id {
+ <$ty>::read($context)?;
+ return Ok(());
+ }
+ )+else {
+ unreachable!()
+ }
+ };
+}
+
+pub fn skip_field_value(
+ context: &mut ReadContext,
+ field_type: &NullableFieldType,
+) -> Result<(), Error> {
+ let ref_flag = context.reader.i8();
+ if field_type.nullable
+ && ref_flag != (RefFlag::NotNullValue as i8)
+ && ref_flag != (RefFlag::RefValue as i8)
+ {
+ return Ok(());
+ }
+ let type_id_num = context.reader.var_uint32();
+ match TypeId::try_from(type_id_num as i16) {
+ Ok(type_id) => {
+ let expected_type_id = field_type.type_id;
+ ensure!(
+ type_id_num == expected_type_id,
+ anyhow!("Invalid field type, expected:{expected_type_id},
actual:{type_id_num}")
+ );
+ if BASIC_TYPES.contains(&type_id) {
+ basic_type_deserialize!(type_id, context;
+ (bool, BOOL),
+ (i8, INT8),
+ (i16, INT16),
+ (i32, INT32),
+ (i64, INT64),
+ (f32, FLOAT32),
+ (f64, FLOAT64),
+ (String, STRING),
+ (NaiveDate, LOCAL_DATE),
+ (NaiveDateTime, TIMESTAMP),
+ );
+ } else if COLLECTION_TYPES.contains(&type_id) {
+ if type_id == TypeId::ARRAY || type_id == TypeId::SET {
+ let length = context.reader.var_int32() as usize;
+ for _ in 0..length {
+ skip_field_value(context,
field_type.generics.first().unwrap())?;
+ }
+ } else if type_id == TypeId::MAP {
+ let length = context.reader.var_int32() as usize;
+ for _ in 0..length {
+ skip_field_value(context,
field_type.generics.first().unwrap())?;
+ skip_field_value(context,
field_type.generics.get(1).unwrap())?;
+ }
+ }
+ Ok(())
+ } else {
+ unreachable!()
+ }
+ }
+ Err(_) => {
+ let tag = type_id_num & 0xff;
+ if tag == TypeId::STRUCT as u32 {
+ let type_def = context.get_meta_by_type_id(type_id_num);
+ let field_infos: Vec<_> = type_def.get_field_infos().to_vec();
+ for field_info in field_infos.iter() {
+ let nullable_field_type =
+ NullableFieldType::from(field_info.field_type.clone());
+ skip_field_value(context, &nullable_field_type)?;
+ }
+ } else {
+ unimplemented!()
+ }
+ Ok(())
+ }
+ }
+}
diff --git a/rust/fory-core/src/serializer/string.rs
b/rust/fory-core/src/serializer/string.rs
index 5c5c3d454..6b0a93582 100644
--- a/rust/fory-core/src/serializer/string.rs
+++ b/rust/fory-core/src/serializer/string.rs
@@ -38,8 +38,8 @@ impl Serializer for String {
Ok(context.reader.string(len as usize))
}
- fn get_type_id(_fory: &Fory) -> i16 {
- TypeId::STRING.into()
+ fn get_type_id(_fory: &Fory) -> u32 {
+ TypeId::STRING as u32
}
}
diff --git a/rust/fory-core/src/types.rs b/rust/fory-core/src/types.rs
index 6128447f0..cd11c1bb2 100644
--- a/rust/fory-core/src/types.rs
+++ b/rust/fory-core/src/types.rs
@@ -125,8 +125,23 @@ pub const BASIC_TYPES: [TypeId; 10] = [
TypeId::TIMESTAMP,
];
+pub const BASIC_TYPE_NAMES: [&str; 10] = [
+ "bool",
+ "i8",
+ "i16",
+ "i32",
+ "i64",
+ "f32",
+ "f64",
+ "String",
+ "NaiveDate",
+ "NaiveDateTime",
+];
+
pub const COLLECTION_TYPES: [TypeId; 3] = [TypeId::ARRAY, TypeId::SET,
TypeId::MAP];
+pub const COLLECTION_TYPE_NAMES: [&str; 3] = ["Vec", "HashSet", "HashMap"];
+
pub fn compute_field_hash(hash: u32, id: i16) -> u32 {
let mut new_hash: u64 = (hash as u64) * 31 + (id as u64);
while new_hash >= MAX_UNT32 {
diff --git a/rust/fory-derive/src/object/derive_enum.rs
b/rust/fory-derive/src/object/derive_enum.rs
index a571dd441..32e91ea49 100644
--- a/rust/fory-derive/src/object/derive_enum.rs
+++ b/rust/fory-derive/src/object/derive_enum.rs
@@ -21,7 +21,7 @@ use syn::DataEnum;
pub fn gen_type_def(_data_enum: &DataEnum) -> TokenStream {
quote! {
- fn type_def(fory: &fory_core::fory::Fory, type_id: i16) -> Vec<u8> {
+ fn type_def(fory: &fory_core::fory::Fory, type_id: u32) -> Vec<u8> {
Vec::new()
}
}
diff --git a/rust/fory-derive/src/object/misc.rs
b/rust/fory-derive/src/object/misc.rs
index 867dd255e..7840a801a 100644
--- a/rust/fory-derive/src/object/misc.rs
+++ b/rust/fory-derive/src/object/misc.rs
@@ -56,7 +56,7 @@ fn type_def(fields: &[&Field]) -> TokenStream {
}
});
quote! {
- fn type_def(fory: &fory_core::fory::Fory, layer_id: i16) -> Vec<u8> {
+ fn type_def(fory: &fory_core::fory::Fory, layer_id: u32) -> Vec<u8> {
fory_core::meta::TypeMeta::from_fields(
layer_id,
vec![#(#field_infos),*]
@@ -76,8 +76,8 @@ pub fn gen_in_struct_impl(fields: &[&Field]) -> TokenStream {
pub fn gen() -> TokenStream {
quote! {
- fn get_type_id(fory: &fory_core::fory::Fory) -> i16 {
-
fory.get_type_resolver().get_type_info(std::any::TypeId::of::<Self>()).get_type_id()
as i16
- }
+ fn get_type_id(fory: &fory_core::fory::Fory) -> u32 {
+
fory.get_type_resolver().get_type_info(std::any::TypeId::of::<Self>()).get_type_id()
+ }
}
}
diff --git a/rust/fory-derive/src/object/read.rs
b/rust/fory-derive/src/object/read.rs
index 3678a2697..442040a51 100644
--- a/rust/fory-derive/src/object/read.rs
+++ b/rust/fory-derive/src/object/read.rs
@@ -17,15 +17,18 @@
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
-use syn::Field;
-use syn::Type;
+use syn::{Field, Type};
-use super::util::{generic_tree_to_tokens, parse_generic_tree};
+use super::util::{generic_tree_to_tokens, parse_generic_tree,
NullableTypeNode};
fn create_private_field_name(field: &Field) -> Ident {
format_ident!("_{}", field.ident.as_ref().expect(""))
}
+fn create_deserialize_nullable_fn_name(field: &Field) -> Ident {
+ format_ident!("_deserialize_nullable_{}", field.ident.as_ref().expect(""))
+}
+
fn bind(fields: &[&Field]) -> Vec<TokenStream> {
fields
.iter()
@@ -70,16 +73,62 @@ fn read(fields: &[&Field]) -> TokenStream {
}
}
-fn deserialize_compatible(fields: &[&Field]) -> TokenStream {
+fn deserialize_compatible(_fields: &[&Field], struct_ident: &Ident) ->
TokenStream {
+ quote! {
+ let ref_flag = context.reader.i8();
+ if ref_flag == (fory_core::types::RefFlag::NotNullValue as i8) ||
ref_flag == (fory_core::types::RefFlag::RefValue as i8) {
+ let type_id = context.reader.var_uint32();
+ #struct_ident::read_compatible(context, type_id)
+ } else if ref_flag == (fory_core::types::RefFlag::Null as i8) {
+ Err(fory_core::error::AnyhowError::msg("Try to deserialize
non-option type to null"))?
+ } else if ref_flag == (fory_core::types::RefFlag::Ref as i8) {
+ Err(fory_core::error::Error::Ref)
+ } else {
+ Err(fory_core::error::AnyhowError::msg("Unknown ref flag,
value:{ref_flag}"))?
+ }
+ }
+}
+
+fn deserialize_nullable(fields: &[&Field]) -> TokenStream {
+ let func_tokens: Vec<TokenStream> = fields
+ .iter()
+ .map(|field| {
+ let fn_name = create_deserialize_nullable_fn_name(field);
+ let ty = &field.ty;
+ let generic_tree = parse_generic_tree(ty);
+ let nullable_generic_tree = NullableTypeNode::from(generic_tree);
+ let deserialize_tokens =
nullable_generic_tree.to_deserialize_tokens(&vec![]);
+ quote! {
+ fn #fn_name(
+ context: &mut fory_core::resolver::context::ReadContext,
+ local_nullable_type: &fory_core::meta::NullableFieldType,
+ remote_nullable_type: &fory_core::meta::NullableFieldType
+ ) -> Result<#ty, fory_core::error::Error> {
+ // println!("remote:{:#?}", remote_nullable_type);
+ #deserialize_tokens
+ }
+ }
+ })
+ .collect::<Vec<_>>();
+ quote! {
+ #(#func_tokens)*
+ }
+}
+
+pub fn gen_nullable(fields: &[&Field]) -> TokenStream {
+ deserialize_nullable(fields)
+}
+
+pub fn gen_read_compatible(fields: &[&Field], struct_ident: &Ident) ->
TokenStream {
let pattern_items = fields.iter().map(|field| {
let ty = &field.ty;
let var_name = create_private_field_name(field);
+ let deserialize_nullable_fn_name =
create_deserialize_nullable_fn_name(field);
let generic_tree = parse_generic_tree(ty);
let generic_token = generic_tree_to_tokens(&generic_tree, true);
let field_name_str = field.ident.as_ref().unwrap().to_string();
-
let base_ty = match &ty {
Type::Path(type_path) => {
&type_path.path.segments.first().unwrap().ident
@@ -87,52 +136,62 @@ fn deserialize_compatible(fields: &[&Field]) ->
TokenStream {
_ => panic!("Unsupported type"),
};
quote! {
- (ident, field_type)
- if ident == #field_name_str
- && *field_type == #generic_token
- => {
- #var_name = Some(<#ty as
fory_core::serializer::Serializer>::deserialize(context).unwrap_or_else(|_err| {
- // println!("skip deserialize {:?}", ident);
- #base_ty::default()
- }));
+ if _field.field_name.as_str() == #field_name_str {
+ let local_field_type = #generic_token;
+ if &_field.field_type == &local_field_type {
+ #var_name = Some(<#ty as
fory_core::serializer::Serializer>::deserialize(context).unwrap_or_else(|_err| {
+ // same type, err means something wrong
+ panic!("Err at deserializing {:?}: {:?}",
#field_name_str, _err);
+ }));
+ } else {
+ let local_nullable_type =
fory_core::meta::NullableFieldType::from(local_field_type.clone());
+ let remote_nullable_type =
fory_core::meta::NullableFieldType::from(_field.field_type.clone());
+ if local_nullable_type != remote_nullable_type {
+ // set default and skip bytes
+ println!("Type not match, just skip: {}",
#field_name_str);
+ fory_core::serializer::skip::skip_field_value(context,
&remote_nullable_type).unwrap();
+ #var_name = Some(#base_ty::default());
+ } else {
+ println!("Try to deserialize: {}", #field_name_str);
+ #var_name = Some(
+ #struct_ident::#deserialize_nullable_fn_name(
+ context,
+ &local_nullable_type,
+ &remote_nullable_type
+ ).unwrap_or_else(|_err| {
+ // same nulable type, err means something wrong
+ panic!("Err at deserializing {:?}: {:?}",
#field_name_str, _err);
+ })
+ );
+ }
+ }
}
}
});
let bind: Vec<TokenStream> = bind(fields);
let create: Vec<TokenStream> = create(fields);
quote! {
- let ref_flag = context.reader.i8();
- if ref_flag == (fory_core::types::RefFlag::NotNullValue as i8) ||
ref_flag == (fory_core::types::RefFlag::RefValue as i8) {
- let meta_index = context.reader.i16() as usize;
- let meta = context.get_meta(meta_index).clone();
+ fn read_compatible(context: &mut
fory_core::resolver::context::ReadContext, type_id: u32) -> Result<Self,
fory_core::error::Error> {
+ let meta = context.get_meta_by_type_id(type_id);
let fields = meta.get_field_infos();
#(#bind)*
for _field in fields.iter() {
- match (_field.field_name.as_str(), &_field.field_type) {
- #(#pattern_items),*
- _ => {
- // skip bytes
- println!("no need to deserialize {:?}:{:?}",
_field.field_name.as_str(), _field.field_type);
-
fory_core::serializer::nonexistent::skip_field_value(context,
&_field.field_type).unwrap();
- }
+ #(#pattern_items else)* {
+ println!("skip {:?}:{:?}", _field.field_name.as_str(),
_field.field_type);
+ let nullable_field_type =
fory_core::meta::NullableFieldType::from(_field.field_type.clone());
+ fory_core::serializer::skip::skip_field_value(context,
&nullable_field_type).unwrap();
}
}
Ok(Self {
#(#create),*
})
- } else if ref_flag == (fory_core::types::RefFlag::Null as i8) {
- Err(fory_core::error::AnyhowError::msg("Try to deserialize
non-option type to null"))?
- } else if ref_flag == (fory_core::types::RefFlag::Ref as i8) {
- Err(fory_core::error::Error::Ref)
- } else {
- Err(fory_core::error::AnyhowError::msg("Unknown ref flag,
value:{ref_flag}"))?
}
}
}
-pub fn gen(fields: &[&Field]) -> TokenStream {
+pub fn gen(fields: &[&Field], struct_ident: &Ident) -> TokenStream {
let read_token_stream = read(fields);
- let compatible_token_stream = deserialize_compatible(fields);
+ let compatible_token_stream = deserialize_compatible(fields, struct_ident);
quote! {
fn deserialize(context: &mut
fory_core::resolver::context::ReadContext) -> Result<Self,
fory_core::error::Error> {
diff --git a/rust/fory-derive/src/object/serializer.rs
b/rust/fory-derive/src/object/serializer.rs
index a3be005d3..cf5195dbe 100644
--- a/rust/fory-derive/src/object/serializer.rs
+++ b/rust/fory-derive/src/object/serializer.rs
@@ -22,19 +22,29 @@ use quote::quote;
pub fn derive_serializer(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
- let (type_def_token_stream, write_token_stream, read_token_stream) = match
&ast.data {
+ let (
+ type_def_token_stream,
+ write_token_stream,
+ read_token_stream,
+ read_compatible_token_stream,
+ read_nullable_token_stream,
+ ) = match &ast.data {
syn::Data::Struct(s) => {
let fields = sorted_fields(&s.fields);
(
misc::gen_in_struct_impl(&fields),
write::gen(&fields),
- read::gen(&fields),
+ read::gen(&fields, name),
+ read::gen_read_compatible(&fields, name),
+ read::gen_nullable(&fields),
)
}
syn::Data::Enum(s) => (
derive_enum::gen_type_def(s),
derive_enum::gen_write(s),
derive_enum::gen_read(s),
+ quote! {},
+ quote! {},
),
syn::Data::Union(_) => {
panic!("Union is not supported")
@@ -53,6 +63,10 @@ pub fn derive_serializer(ast: &syn::DeriveInput) ->
TokenStream {
#write_token_stream
#read_token_stream
}
+ impl #name {
+ #read_compatible_token_stream
+ #read_nullable_token_stream
+ }
};
gen.into()
}
diff --git a/rust/fory-derive/src/object/util.rs
b/rust/fory-derive/src/object/util.rs
index 8981ddaee..93b2a4880 100644
--- a/rust/fory-derive/src/object/util.rs
+++ b/rust/fory-derive/src/object/util.rs
@@ -15,17 +15,288 @@
// specific language governing permissions and limitations
// under the License.
-use fory_core::types::TypeId;
+use fory_core::types::{TypeId, BASIC_TYPE_NAMES, COLLECTION_TYPE_NAMES};
use proc_macro2::TokenStream;
use quote::quote;
use std::fmt;
-use syn::{GenericArgument, PathArguments, Type};
+use syn::{parse_str, GenericArgument, PathArguments, Type};
pub(super) struct TypeNode {
name: String,
generics: Vec<TypeNode>,
}
+pub(super) struct NullableTypeNode {
+ name: String,
+ generics: Vec<NullableTypeNode>,
+ nullable: bool,
+}
+
+macro_rules! basic_type_deserialize {
+ ($name:expr, $nullable:expr; $( ($ty_str:expr, $ty:ty) ),* $(,)?) => {
+ match $name {
+ $(
+ $ty_str => {
+ if $nullable {
+ quote! {
+ let res1 = if cur_remote_nullable_type.nullable &&
ref_flag == (fory_core::types::RefFlag::Null as i8) {
+ None
+ } else {
+ let _type_id = context.reader.var_uint32();
+ Some(<$ty as
fory_core::serializer::Serializer>::read(context)
+ .map_err(fory_core::error::Error::from)?)
+ };
+ Ok::<Option<$ty>, fory_core::error::Error>(res1)
+ }
+ } else {
+ quote! {
+ let res2 = if cur_remote_nullable_type.nullable &&
ref_flag == (fory_core::types::RefFlag::Null as i8) {
+ $ty::default()
+ } else {
+ let _type_id = context.reader.var_uint32();
+ <$ty as
fory_core::serializer::Serializer>::read(context)
+ .map_err(fory_core::error::Error::from)?
+ };
+ Ok::<$ty, fory_core::error::Error>(res2)
+ }
+ }
+ }
+ )*
+ _ => unreachable!(),
+ }
+ };
+}
+
+impl NullableTypeNode {
+ pub(super) fn to_deserialize_tokens(&self, generic_path: &Vec<i8>) ->
TokenStream {
+ let tokens = if BASIC_TYPE_NAMES.contains(&self.name.as_str()) {
+ basic_type_deserialize!(self.name.as_str(), self.nullable;
+ ("bool", bool),
+ ("i8", i8),
+ ("i16", i16),
+ ("i32", i32),
+ ("i64", i64),
+ ("f32", f32),
+ ("f64", f64),
+ ("String", String),
+ ("NaiveDate", chrono::NaiveDate),
+ ("NaiveDateTime", chrono::NaiveDateTime),
+ )
+ } else if COLLECTION_TYPE_NAMES.contains(&self.name.as_str()) {
+ let ty = parse_str::<Type>(&self.to_string()).unwrap();
+ let mut new_path = generic_path.clone();
+ match self.name.as_str() {
+ "Vec" => {
+ let generic_node = self.generics.first().unwrap();
+ new_path.push(0);
+ let element_tokens =
generic_node.to_deserialize_tokens(&new_path);
+ let element_ty: Type =
parse_str(&generic_node.to_string()).unwrap();
+ if self.nullable {
+ quote! {
+ let v = if cur_remote_nullable_type.nullable &&
ref_flag == (fory_core::types::RefFlag::Null as i8) {
+ None
+ } else {
+ let _arr_type_id = context.reader.var_uint32();
+ let length = context.reader.var_int32() as
usize;
+ let mut v = Vec::with_capacity(length);
+ for _ in 0..length {
+ let element: #element_ty =
{#element_tokens}?;
+ v.push(element);
+ }
+ Some(v)
+ };
+ Ok::<#ty, fory_core::error::Error>(v)
+ }
+ } else {
+ quote! {
+ let v = if cur_remote_nullable_type.nullable &&
ref_flag == (fory_core::types::RefFlag::Null as i8) {
+ Vec::default()
+ } else {
+ let _arr_type_id = context.reader.var_uint32();
+ let length = context.reader.var_int32() as
usize;
+ let mut v = Vec::with_capacity(length);
+ for _ in 0..length {
+ let element: #element_ty =
{#element_tokens}?;
+ v.push(element);
+ }
+ v
+ };
+ Ok::<#ty, fory_core::error::Error>(v)
+ }
+ }
+ }
+ "HashSet" => {
+ let generic_node = self.generics.first().unwrap();
+ new_path.push(0);
+ let element_tokens =
generic_node.to_deserialize_tokens(&new_path);
+ let element_ty: Type =
parse_str(&generic_node.to_string()).unwrap();
+ if self.nullable {
+ quote! {
+ let s = if cur_remote_nullable_type.nullable &&
ref_flag == (fory_core::types::RefFlag::Null as i8) {
+ None
+ } else {
+ let _set_type_id = context.reader.var_uint32();
+ let length = context.reader.var_int32() as
usize;
+ let mut s = HashSet::with_capacity(length);
+ for _ in 0..length {
+ let element: #element_ty =
{#element_tokens}?;
+ s.insert(element);
+ }
+ Some(s)
+ };
+ Ok::<#ty, fory_core::error::Error>(s)
+ }
+ } else {
+ quote! {
+ let s = if cur_remote_nullable_type.nullable &&
ref_flag == (fory_core::types::RefFlag::Null as i8) {
+ HashSet::default()
+ } else {
+ let _set_type_id = context.reader.var_uint32();
+ let length = context.reader.var_int32() as
usize;
+ let mut s = HashSet::with_capacity(length);
+ for _ in 0..length {
+ let element: #element_ty =
{#element_tokens}?;
+ s.insert(element);
+ }
+ s
+ };
+ Ok::<#ty, fory_core::error::Error>(s)
+ }
+ }
+ }
+ "HashMap" => {
+ let key_generic_node = self.generics.first().unwrap();
+ let val_generic_node = self.generics.get(1).unwrap();
+ new_path.push(0);
+ let key_tokens =
key_generic_node.to_deserialize_tokens(&new_path);
+ new_path.pop();
+ new_path.push(1);
+ let val_tokens =
val_generic_node.to_deserialize_tokens(&new_path);
+ let key_ty: Type =
parse_str(&key_generic_node.to_string()).unwrap();
+ let val_ty: Type =
parse_str(&val_generic_node.to_string()).unwrap();
+ if self.nullable {
+ quote! {
+ let m = if cur_remote_nullable_type.nullable &&
ref_flag == (fory_core::types::RefFlag::Null as i8) {
+ None
+ } else {
+ let _map_type_id = context.reader.var_uint32();
+ let length = context.reader.var_int32() as
usize;
+ let mut m = HashMap::with_capacity(length);
+ for _ in 0..length {
+ let key: #key_ty = {#key_tokens}?;
+ let value: #val_ty = {#val_tokens}?;
+ m.insert(key, value);
+ }
+ Some(m)
+ };
+ Ok::<#ty, fory_core::error::Error>(m)
+ }
+ } else {
+ quote! {
+ let m = if cur_remote_nullable_type.nullable &&
ref_flag == (fory_core::types::RefFlag::Null as i8) {
+ HashMap::default()
+ } else {
+ let _map_type_id = context.reader.var_uint32();
+ let length = context.reader.var_int32() as
usize;
+ let mut m = HashMap::with_capacity(length);
+ for _ in 0..length {
+ let key: #key_ty = {#key_tokens}?;
+ let value: #val_ty = {#val_tokens}?;
+ m.insert(key, value);
+ }
+ m
+ };
+ Ok::<#ty, fory_core::error::Error>(m)
+ }
+ }
+ }
+ _ => quote! { compile_error!("Unsupported type for
collection"); },
+ }
+ } else {
+ // struct
+ let nullable_ty =
parse_str::<Type>(&self.nullable_ty_string()).unwrap();
+ let ty = parse_str::<Type>(&self.to_string()).unwrap();
+ if self.nullable {
+ quote! {
+ let res1 = if cur_remote_nullable_type.nullable &&
ref_flag == (fory_core::types::RefFlag::Null as i8) {
+ None
+ } else {
+ let type_id = context.reader.var_uint32();
+ let internal_id = type_id & 0xff;
+ assert_eq!(internal_id as i16,
fory_core::types::TypeId::STRUCT as i16);
+ Some(#nullable_ty::read_compatible(context, type_id)
+ .map_err(fory_core::error::Error::from)?)
+ };
+ Ok::<#ty, fory_core::error::Error>(res1)
+ }
+ } else {
+ quote! {
+ let res2 = if cur_remote_nullable_type.nullable &&
ref_flag == (fory_core::types::RefFlag::Null as i8) {
+ #ty::default()
+ } else {
+ let type_id = context.reader.var_uint32();
+ let internal_id = type_id & 0xff;
+ assert_eq!(internal_id as i16,
fory_core::types::TypeId::STRUCT as i16);
+ <#nullable_ty>::read_compatible(context, type_id)
+ .map_err(fory_core::error::Error::from)?
+ };
+ Ok::<#ty, fory_core::error::Error>(res2)
+ }
+ }
+ };
+ let mut cur_remote_nullable_type = quote! { remote_nullable_type };
+ for idx in generic_path {
+ cur_remote_nullable_type = quote! {
+ #cur_remote_nullable_type.generics.get(#idx as usize).unwrap()
+ };
+ }
+ quote! {
+ let cur_remote_nullable_type = &#cur_remote_nullable_type;
+ let ref_flag = context.reader.i8();
+ #tokens
+ }
+ }
+
+ pub(super) fn from(node: TypeNode) -> Self {
+ if node.name == "Option" {
+ let inner =
NullableTypeNode::from(node.generics.into_iter().next().unwrap());
+ NullableTypeNode {
+ name: inner.name,
+ generics: inner.generics,
+ nullable: true,
+ }
+ } else {
+ let generics = node
+ .generics
+ .into_iter()
+ .map(NullableTypeNode::from)
+ .collect();
+
+ NullableTypeNode {
+ name: node.name,
+ generics,
+ nullable: false,
+ }
+ }
+ }
+
+ pub(super) fn nullable_ty_string(&self) -> String {
+ if self.generics.is_empty() {
+ self.name.clone()
+ } else {
+ format!(
+ "{}<{}>",
+ self.name,
+ self.generics
+ .iter()
+ .map(|g| g.to_string())
+ .collect::<Vec<_>>()
+ .join(",")
+ )
+ }
+ }
+}
+
impl fmt::Display for TypeNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.generics.is_empty() {
@@ -45,6 +316,30 @@ impl fmt::Display for TypeNode {
}
}
+impl fmt::Display for NullableTypeNode {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let inner_type = if self.generics.is_empty() {
+ self.name.clone()
+ } else {
+ format!(
+ "{}<{}>",
+ self.name,
+ self.generics
+ .iter()
+ .map(|g| g.to_string())
+ .collect::<Vec<_>>()
+ .join(",")
+ )
+ };
+
+ if self.nullable {
+ write!(f, "Option<{}>", inner_type)
+ } else {
+ write!(f, "{}", inner_type)
+ }
+ }
+}
+
fn extract_type_name(ty: &Type) -> String {
if let Type::Path(type_path) = ty {
type_path.path.segments.last().unwrap().ident.to_string()
@@ -80,6 +375,11 @@ pub(super) fn parse_generic_tree(ty: &Type) -> TypeNode {
}
pub(super) fn generic_tree_to_tokens(node: &TypeNode, have_context: bool) ->
TokenStream {
+ if node.name == "Option" && node.generics.first().unwrap().name ==
"Option" {
+ return quote! {
+ compile_error!("adjacent Options are not supported");
+ };
+ }
let children_tokens: Vec<TokenStream> = node
.generics
.iter()
@@ -96,7 +396,7 @@ pub(super) fn generic_tree_to_tokens(node: &TypeNode,
have_context: bool) -> Tok
}
};
let get_type_id = if node.name == "Option" {
- let option_type_id: i16 = TypeId::ForyOption.into();
+ let option_type_id = TypeId::ForyOption as u32;
quote! {
#option_type_id
}
diff --git a/rust/fory-derive/src/object/write.rs
b/rust/fory-derive/src/object/write.rs
index 44ec8cd2a..05c40de7d 100644
--- a/rust/fory-derive/src/object/write.rs
+++ b/rust/fory-derive/src/object/write.rs
@@ -44,10 +44,8 @@ pub fn gen(fields: &[&Field]) -> TokenStream {
},
fory_core::types::Mode::Compatible => {
context.writer.i8(fory_core::types::RefFlag::NotNullValue
as i8);
- let meta_index = context.push_meta(
- std::any::TypeId::of::<Self>()
- ) as i16;
- context.writer.i16(meta_index);
+ let type_id = Self::get_type_id(context.get_fory());
+ context.writer.var_uint32(type_id);
self.write(context);
}
}
@@ -55,6 +53,9 @@ pub fn gen(fields: &[&Field]) -> TokenStream {
fn write(&self, context: &mut
fory_core::resolver::context::WriteContext) {
+ let _meta_index = context.push_meta(
+ std::any::TypeId::of::<Self>()
+ ) as i16;
// write fields
#(#accessor_expr)*
}
diff --git a/rust/tests/tests/test_buffer.rs b/rust/tests/tests/test_buffer.rs
index e1dcdb887..16370406b 100644
--- a/rust/tests/tests/test_buffer.rs
+++ b/rust/tests/tests/test_buffer.rs
@@ -48,4 +48,12 @@ fn test_var_int32() {
let res = reader.var_int32();
assert_eq!(res, data);
}
+ for &data in &test_data {
+ let mut writer = Writer::default();
+ writer.var_uint32(data as u32);
+ let binding = writer.dump();
+ let mut reader = Reader::new(binding.as_slice());
+ let res = reader.var_uint32();
+ assert_eq!(res, data as u32);
+ }
}
diff --git a/rust/tests/tests/test_compatible.rs
b/rust/tests/tests/test_compatible.rs
index d9d94d72b..9bd1263a0 100644
--- a/rust/tests/tests/test_compatible.rs
+++ b/rust/tests/tests/test_compatible.rs
@@ -18,8 +18,7 @@
use fory_core::fory::Fory;
use fory_core::types::Mode::Compatible;
use fory_derive::Fory;
-use std::collections::HashMap;
-
+use std::collections::{HashMap, HashSet};
// RUSTFLAGS="-Awarnings" cargo expand -p fory-tests --test test_compatible
#[test]
fn simple() {
@@ -46,7 +45,6 @@ fn simple() {
f7: i16,
f8: i8,
}
-
let mut fory1 = Fory::default().mode(Compatible);
let mut fory2 = Fory::default().mode(Compatible);
fory1.register::<Animal1>(999);
@@ -72,6 +70,38 @@ fn simple() {
assert_eq!(animal.f8, obj.f8);
}
+#[test]
+fn skip_option() {
+ #[derive(Fory, Debug)]
+ struct Item1 {
+ f1: Option<String>,
+ f2: Option<String>,
+ last: i64,
+ }
+
+ #[derive(Fory, Debug)]
+ struct Item2 {
+ f1: i8,
+ f2: i8,
+ last: i64,
+ }
+ let mut fory1 = Fory::default().mode(Compatible);
+ let mut fory2 = Fory::default().mode(Compatible);
+ fory1.register::<Item1>(999);
+ fory2.register::<Item2>(999);
+ let item1 = Item1 {
+ f1: None,
+ f2: Some(String::from("f2")),
+ last: 42,
+ };
+ let bin = fory1.serialize(&item1);
+ let item2: Item2 = fory2.deserialize(&bin).unwrap();
+
+ assert_eq!(item2.f1, i8::default());
+ assert_eq!(item2.f2, i8::default());
+ assert_eq!(item2.last, item1.last)
+}
+
#[test]
fn nonexistent_struct() {
#[derive(Fory, Debug, Default)]
@@ -86,13 +116,13 @@ fn nonexistent_struct() {
struct Person1 {
f2: Item1,
f3: i8,
- f4: String,
+ last: String,
}
#[derive(Fory, Debug)]
struct Person2 {
f2: Item2,
f3: i64,
- f4: String,
+ last: String,
}
let mut fory1 = Fory::default().mode(Compatible);
let mut fory2 = Fory::default().mode(Compatible);
@@ -103,13 +133,13 @@ fn nonexistent_struct() {
let person = Person1 {
f2: Item1 { f1: 42 },
f3: 24,
- f4: String::from("foo"),
+ last: String::from("foo"),
};
let bin = fory1.serialize(&person);
let obj: Person2 = fory2.deserialize(&bin).unwrap();
assert_eq!(obj.f2, Item2::default());
assert_eq!(obj.f3, i64::default());
- assert_eq!(obj.f4, person.f4);
+ assert_eq!(obj.last, person.last);
}
#[test]
@@ -122,6 +152,7 @@ fn option() {
// adjacent Options are not supported
// f4: Option<Option<String>>,
f5: Vec<Option<Vec<Option<String>>>>,
+ last: i64,
}
let mut fory = Fory::default().mode(Compatible);
fory.register::<Animal>(999);
@@ -131,12 +162,217 @@ fn option() {
f3: vec![Option::<String>::None, Some(String::from("f3"))],
// f4: Some(Some(String::from("f4"))),
f5: vec![Some(vec![Some(String::from("f1"))])],
+ last: 666,
};
let bin = fory.serialize(&animal);
let obj: Animal = fory.deserialize(&bin).unwrap();
assert_eq!(animal, obj);
}
+#[test]
+fn nullable() {
+ /*
+ f1: value -> value
+ f2: value -> Option(value)
+ f3: Option(value) -> value
+ f4: Option(value) -> Option(value)
+ f5: Option(None) -> Option(None)
+ f6: Option(None) -> value_default
+ */
+ #[derive(Fory, Debug, Default)]
+ pub struct Item1 {
+ f2: i8,
+ f3: Option<i8>,
+ f4: Option<i8>,
+ f5: Option<i8>,
+ f6: Option<i8>,
+ last: i64,
+ }
+
+ #[derive(Fory, Debug, Default)]
+ pub struct Item2 {
+ f2: Option<i8>,
+ f3: i8,
+ f4: Option<i8>,
+ f5: Option<i8>,
+ f6: i8,
+ last: i64,
+ }
+
+ let mut fory1 = Fory::default().mode(Compatible);
+ let mut fory2 = Fory::default().mode(Compatible);
+ fory1.register::<Item1>(999);
+ fory2.register::<Item2>(999);
+
+ let item1 = Item1 {
+ f2: 43,
+ f3: Some(44),
+ f4: Some(45),
+ f5: None,
+ f6: None,
+ last: 666,
+ };
+
+ let bin = fory1.serialize(&item1);
+ let item2: Item2 = fory2.deserialize(&bin).unwrap();
+ assert_eq!(item2.f2.unwrap(), item1.f2);
+ assert_eq!(item2.f3, item1.f3.unwrap());
+ assert_eq!(item2.f4, item1.f4);
+ assert_eq!(item2.f5, item1.f5);
+ assert_eq!(item2.f6, i8::default());
+ assert_eq!(item2.last, item1.last);
+}
+
+#[test]
+fn nullable_collection() {
+ #[derive(Fory, Debug, Default)]
+ pub struct Item1 {
+ f1: Vec<i8>,
+ f2: Option<Vec<i8>>,
+ f3: HashSet<i8>,
+ f4: Option<HashSet<i8>>,
+ f5: HashMap<i8, Vec<i8>>,
+ f6: Option<HashMap<i8, Vec<i8>>>,
+ f7: Option<Vec<i8>>,
+ f8: Option<HashSet<i8>>,
+ f9: Option<HashMap<i8, i8>>,
+ last: i64,
+ }
+
+ #[derive(Fory, Debug, Default)]
+ pub struct Item2 {
+ f1: Option<Vec<i8>>,
+ f2: Vec<i8>,
+ f3: Option<HashSet<i8>>,
+ f4: HashSet<i8>,
+ f5: Option<HashMap<i8, Vec<i8>>>,
+ f6: HashMap<i8, Vec<i8>>,
+ f7: Vec<i8>,
+ f8: HashSet<i8>,
+ f9: HashMap<i8, i8>,
+ last: i64,
+ }
+
+ let mut fory1 = Fory::default().mode(Compatible);
+ let mut fory2 = Fory::default().mode(Compatible);
+ fory1.register::<Item1>(999);
+ fory2.register::<Item2>(999);
+
+ let item1 = Item1 {
+ f1: vec![44, 45],
+ f2: Some(vec![43]),
+ f3: HashSet::from([44, 45]),
+ f4: Some(HashSet::from([46, 47])),
+ f5: HashMap::from([(48, vec![49])]),
+ f6: Some(HashMap::from([(48, vec![49])])),
+ f7: None,
+ f8: None,
+ f9: None,
+ last: 666,
+ };
+
+ let bin = fory1.serialize(&item1);
+ let item2: Item2 = fory2.deserialize(&bin).unwrap();
+
+ assert_eq!(item2.f1.unwrap(), item1.f1);
+ assert_eq!(item2.f2, item1.f2.unwrap());
+ assert_eq!(item2.f3.unwrap(), item1.f3);
+ assert_eq!(item2.f4, item1.f4.unwrap());
+ assert_eq!(item2.f5.unwrap(), item1.f5);
+ assert_eq!(item2.f6, item1.f6.unwrap());
+ assert_eq!(item2.f7, Vec::default());
+ assert_eq!(item2.f8, HashSet::default());
+ assert_eq!(item2.f9, HashMap::default());
+ assert_eq!(item2.last, item1.last);
+}
+
+#[test]
+fn inner_nullable() {
+ #[derive(Fory, Debug, Default)]
+ pub struct Item1 {
+ f1: Vec<Option<i8>>,
+ f2: HashSet<Option<i8>>,
+ f3: HashMap<i8, Option<i8>>,
+ }
+
+ #[derive(Fory, Debug, Default)]
+ pub struct Item2 {
+ f1: Vec<i8>,
+ f2: HashSet<i8>,
+ f3: HashMap<i8, i8>,
+ }
+ let mut fory1 = Fory::default().mode(Compatible);
+ let mut fory2 = Fory::default().mode(Compatible);
+ fory1.register::<Item1>(999);
+ fory2.register::<Item2>(999);
+
+ let item1 = Item1 {
+ f1: vec![None, Some(42)],
+ f2: HashSet::from([None, Some(43)]),
+ f3: HashMap::from([(44, None), (45, Some(46))]),
+ };
+ let bin = fory1.serialize(&item1);
+ let item2: Item2 = fory2.deserialize(&bin).unwrap();
+
+ assert_eq!(item2.f1, vec![0, 42]);
+ assert_eq!(item2.f2, HashSet::from([0, 43]));
+ assert_eq!(item2.f3, HashMap::from([(44, 0), (45, 46)]));
+}
+
+#[test]
+fn nullable_struct() {
+ #[derive(Fory, Debug, Default, PartialEq)]
+ pub struct Item {
+ name: String,
+ data: Vec<Option<i8>>,
+ last: i64,
+ }
+
+ #[derive(Fory, Debug, Default)]
+ pub struct Person1 {
+ f1: Item,
+ f2: Option<Item>,
+ f3: Option<Item>,
+ last: i64,
+ }
+
+ #[derive(Fory, Debug, Default)]
+ pub struct Person2 {
+ f1: Option<Item>,
+ f2: Item,
+ f3: Item,
+ last: i64,
+ }
+ let mut fory1 = Fory::default().mode(Compatible);
+ let mut fory2 = Fory::default().mode(Compatible);
+ fory1.register::<Item>(199);
+ fory1.register::<Person1>(200);
+ fory2.register::<Item>(199);
+ fory2.register::<Person2>(200);
+
+ let person1 = Person1 {
+ f1: Item {
+ name: "f1".to_string(),
+ data: vec![None, Some(42)],
+ last: 43,
+ },
+ f2: None,
+ f3: Some(Item {
+ name: "f3".to_string(),
+ data: vec![None, Some(44)],
+ last: 45,
+ }),
+ last: 46,
+ };
+ let bin = fory1.serialize(&person1);
+ let person2: Person2 = fory2.deserialize(&bin).unwrap();
+
+ assert_eq!(person2.f1.unwrap(), person1.f1);
+ assert_eq!(person2.f2, Item::default());
+ assert_eq!(person2.f3, person1.f3.unwrap());
+ assert_eq!(person2.last, person1.last);
+}
+
// #[test]
// fn not_impl_default() {
// #[derive(Fory, Debug)]
diff --git a/rust/tests/tests/test_complex_struct.rs
b/rust/tests/tests/test_complex_struct.rs
index 461233c30..140154ca0 100644
--- a/rust/tests/tests/test_complex_struct.rs
+++ b/rust/tests/tests/test_complex_struct.rs
@@ -15,10 +15,10 @@
// specific language governing permissions and limitations
// under the License.
-use chrono::{DateTime, NaiveDate, NaiveDateTime};
use fory_core::fory::Fory;
use fory_derive::Fory;
// use std::any::Any;
+use chrono::{DateTime, NaiveDate, NaiveDateTime};
use std::collections::HashMap;
// RUSTFLAGS="-Awarnings" cargo expand -p fory-tests --test test_complex_struct
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]