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 849883045 feat(rust): Support rust tagged union enum (#2855)
849883045 is described below
commit 849883045054aaefb970adc7c9acb2744d789ab9
Author: urlyy <[email protected]>
AuthorDate: Fri Oct 31 00:39:53 2025 +0800
feat(rust): Support rust tagged union enum (#2855)
## Why?
Enum with poayload is the norm in rust and is extensively used.
## What does this PR do?
Modified the `Enum::read_data()`, `Enum::write_data()`, and the code
generation for `Enum::default()` to support tagged union enums.
## Related issues
#2851
## Does this PR introduce any user-facing change?
If the user is in `Rust to Rust` protocol instead of `Xlang` protocol,
the values of the tagged union enum can be transmitted correctly.
Otherwise, in `Xlang` protocol, although the corresponding enum type can
be passed, the `internal value` will be the initial value. Because at
present, the `xlang protocol` only serializes the `ordinal value` of
enums.
For example:
```rust
#[test]
fn test_enum(){
#[derive(ForyObject, Debug, PartialEq)]
enum Color {
Red(i8),
White(String)
}
let color = Color::Red(10);
// rust protocol
let mut fory = Fory::default().xlang(false);
fory.register::<Color>(1000).unwrap();
let bin = fory.serialize(&color).unwrap();
let new_color = fory.deserialize::<Color>(&bin).unwrap();
assert_eq!(color, new_color);
if let Color::Red(value) = new_color {
assert_eq!(value, 10);
} else {
unreachable!()
}
// xlang protocol
let mut fory2 = Fory::default().xlang(true);
fory2.register::<Color>(1000).unwrap();
let bin = fory2.serialize(&color).unwrap();
let new_color = fory2.deserialize::<Color>(&bin).unwrap();
if let Color::Red(value) = new_color {
assert_eq!(value, i8::default());
} else {
unreachable!()
}
}
```
---
rust/fory-derive/src/object/derive_enum.rs | 261 +++++++++++++++++++++++++++--
rust/fory-derive/src/object/mod.rs | 2 +-
rust/fory-derive/src/object/read.rs | 2 +-
rust/fory-derive/src/object/serializer.rs | 18 +-
rust/fory-derive/src/object/write.rs | 47 ++++--
rust/tests/tests/test_enum.rs | 101 +++++++++++
6 files changed, 391 insertions(+), 40 deletions(-)
diff --git a/rust/fory-derive/src/object/derive_enum.rs
b/rust/fory-derive/src/object/derive_enum.rs
index 0027646d8..c7a96c422 100644
--- a/rust/fory-derive/src/object/derive_enum.rs
+++ b/rust/fory-derive/src/object/derive_enum.rs
@@ -15,9 +15,16 @@
// specific language governing permissions and limitations
// under the License.
-use proc_macro2::TokenStream;
+use crate::object::read::gen_read_field;
+
+use crate::object::write::gen_write_field;
+use proc_macro2::{Ident, TokenStream};
use quote::quote;
-use syn::DataEnum;
+use syn::{DataEnum, Fields};
+
+fn temp_var_name(i: usize) -> String {
+ format!("f{}", i)
+}
pub fn gen_actual_type_id() -> TokenStream {
quote! {
@@ -44,18 +51,122 @@ pub fn gen_write(_data_enum: &DataEnum) -> TokenStream {
}
pub fn gen_write_data(data_enum: &DataEnum) -> TokenStream {
- let variant_idents: Vec<_> = data_enum.variants.iter().map(|v|
&v.ident).collect();
- let variant_values: Vec<_> = (0..variant_idents.len()).map(|v| v as
u32).collect();
- quote! {
- Ok(match self {
- #(
- Self::#variant_idents => {
- context.writer.write_varuint32(#variant_values);
+ let xlang_variant_branches: Vec<TokenStream> = data_enum
+ .variants
+ .iter()
+ .enumerate()
+ .map(|(idx, v)| {
+ let ident = &v.ident;
+ let tag_value = idx as u32;
+
+ match &v.fields {
+ Fields::Unit => {
+ quote! {
+ Self::#ident => {
+ context.writer.write_varuint32(#tag_value);
+ }
+ }
+ }
+ Fields::Unnamed(_) => {
+ quote! {
+ Self::#ident(..) => {
+ context.writer.write_varuint32(#tag_value);
+ }
+ }
}
- )*
+ Fields::Named(_) => {
+ quote! {
+ Self::#ident { .. } => {
+ context.writer.write_varuint32(#tag_value);
+ }
+ }
+ }
+ }
})
+ .collect();
+
+ let rust_variant_branches: Vec<TokenStream> = data_enum
+ .variants
+ .iter()
+ .enumerate()
+ .map(|(idx, v)| {
+ let ident = &v.ident;
+ let tag_value = idx as u32;
+
+ match &v.fields {
+ Fields::Unit => {
+ quote! {
+ Self::#ident => {
+ context.writer.write_varuint32(#tag_value);
+ }
+ }
+ }
+ Fields::Unnamed(fields_unnamed) => {
+ let field_idents: Vec<_> =
(0..fields_unnamed.unnamed.len())
+ .map(|i| Ident::new(&temp_var_name(i),
proc_macro2::Span::call_site()))
+ .collect();
+
+ let write_fields: Vec<_> = fields_unnamed
+ .unnamed
+ .iter()
+ .zip(field_idents.iter())
+ .map(|(f, ident)| gen_write_field(f, ident, false))
+ .collect();
+
+ quote! {
+ Self::#ident( #(#field_idents),* ) => {
+ context.writer.write_varuint32(#tag_value);
+ #(#write_fields)*
+ }
+ }
+ }
+ Fields::Named(fields_named) => {
+ let mut sorted_fields: Vec<_> =
fields_named.named.iter().collect();
+ sorted_fields.sort_by(|a, b| {
+ a.ident
+ .as_ref()
+ .unwrap()
+ .to_string()
+ .cmp(&b.ident.as_ref().unwrap().to_string())
+ });
+
+ let field_idents: Vec<_> = sorted_fields
+ .iter()
+ .map(|f| f.ident.as_ref().unwrap())
+ .collect();
+
+ let write_fields: Vec<_> = sorted_fields
+ .iter()
+ .zip(field_idents.iter())
+ .map(|(f, ident)| gen_write_field(f, ident, false))
+ .collect();
+
+ quote! {
+ Self::#ident { #(#field_idents),* } => {
+ context.writer.write_varuint32(#tag_value) ;
+ #(#write_fields)*
+ }
+ }
+ }
+ }
+ })
+ .collect();
+
+ quote! {
+ if context.is_xlang() {
+ match self {
+ #(#xlang_variant_branches)*
+ }
+ Ok(())
+ } else {
+ match self {
+ #(#rust_variant_branches)*
+ }
+ Ok(())
+ }
}
}
+
pub fn gen_write_type_info() -> TokenStream {
quote! {
fory_core::serializer::enum_::write_type_info::<Self>(context)
@@ -75,15 +186,129 @@ pub fn gen_read_with_type_info(_: &DataEnum) ->
TokenStream {
}
pub fn gen_read_data(data_enum: &DataEnum) -> TokenStream {
- let variant_idents: Vec<_> = data_enum.variants.iter().map(|v|
&v.ident).collect();
- let variant_values: Vec<_> = (0..variant_idents.len()).map(|v| v as
u32).collect();
+ let xlang_variant_branches: Vec<TokenStream> = data_enum
+ .variants
+ .iter()
+ .enumerate()
+ .map(|(idx, v)| {
+ let ident = &v.ident;
+ let tag_value = idx as u32;
+
+ match &v.fields {
+ Fields::Unit => {
+ quote! {
+ #tag_value => Ok(Self::#ident),
+ }
+ }
+ Fields::Unnamed(fields_unnamed) => {
+ let default_fields: Vec<TokenStream> = fields_unnamed
+ .unnamed
+ .iter()
+ .map(|_| {
+ quote! { Default::default() }
+ })
+ .collect();
+
+ quote! {
+ #tag_value => Ok(Self::#ident( #(#default_fields),* )),
+ }
+ }
+ Fields::Named(fields_named) => {
+ let default_fields: Vec<TokenStream> = fields_named
+ .named
+ .iter()
+ .map(|f| {
+ let field_ident = f.ident.as_ref().unwrap();
+ quote! { #field_ident: Default::default() }
+ })
+ .collect();
+
+ quote! {
+ #tag_value => Ok(Self::#ident { #(#default_fields),*
}),
+ }
+ }
+ }
+ })
+ .collect();
+
+ let rust_variant_branches: Vec<TokenStream> = data_enum
+ .variants
+ .iter()
+ .enumerate()
+ .map(|(idx, v)| {
+ let ident = &v.ident;
+ let tag_value = idx as u32;
+
+ match &v.fields {
+ Fields::Unit => {
+ quote! {
+ #tag_value => Ok(Self::#ident),
+ }
+ }
+ Fields::Unnamed(fields_unnamed) => {
+ let field_idents: Vec<Ident> =
(0..fields_unnamed.unnamed.len())
+ .map(|i| Ident::new(&temp_var_name(i),
proc_macro2::Span::call_site()))
+ .collect();
+
+ let read_fields: Vec<TokenStream> = fields_unnamed
+ .unnamed
+ .iter()
+ .zip(field_idents.iter())
+ .map(|(f, ident)| gen_read_field(f, ident))
+ .collect();
+
+ quote! {
+ #tag_value => {
+ #(#read_fields;)*
+ Ok(Self::#ident( #(#field_idents),* ))
+ }
+ }
+ }
+ Fields::Named(fields_named) => {
+ let mut sorted_fields: Vec<_> =
fields_named.named.iter().collect();
+ sorted_fields.sort_by(|a, b| {
+ a.ident
+ .as_ref()
+ .unwrap()
+ .to_string()
+ .cmp(&b.ident.as_ref().unwrap().to_string())
+ });
+
+ let field_idents: Vec<_> = sorted_fields
+ .iter()
+ .map(|f| f.ident.as_ref().unwrap())
+ .collect();
+
+ let read_fields: Vec<_> = sorted_fields
+ .iter()
+ .zip(field_idents.iter())
+ .map(|(f, ident)| gen_read_field(f, ident))
+ .collect();
+
+ quote! {
+ #tag_value => {
+ #(#read_fields;)*
+ Ok(Self::#ident { #(#field_idents),* })
+ }
+ }
+ }
+ }
+ })
+ .collect();
+
quote! {
- let ordinal = context.reader.read_varuint32()?;
- match ordinal {
- #(
- #variant_values => Ok(Self::#variant_idents),
- )*
- _ => return Err(fory_core::error::Error::unknown_enum("unknown enum
value")),
+ if context.is_xlang() {
+ let ordinal = context.reader.read_varuint32()?;
+ match ordinal {
+ #(#xlang_variant_branches)*
+ _ => return Err(fory_core::error::Error::unknown_enum("unknown
enum value")),
+ }
+ } else {
+ let tag = context.reader.read_varuint32()?;
+ match tag {
+ #(#rust_variant_branches)*
+ _ => return Err(fory_core::error::Error::unknown_enum("unknown
enum value")),
+ }
}
}
}
diff --git a/rust/fory-derive/src/object/mod.rs
b/rust/fory-derive/src/object/mod.rs
index 5e25ce09f..a2529d1fe 100644
--- a/rust/fory-derive/src/object/mod.rs
+++ b/rust/fory-derive/src/object/mod.rs
@@ -17,7 +17,7 @@
mod derive_enum;
mod misc;
-mod read;
+pub(crate) mod read;
mod serializer;
pub(crate) mod util;
mod write;
diff --git a/rust/fory-derive/src/object/read.rs
b/rust/fory-derive/src/object/read.rs
index 8bc8a4bc7..b7c4cde18 100644
--- a/rust/fory-derive/src/object/read.rs
+++ b/rust/fory-derive/src/object/read.rs
@@ -101,7 +101,7 @@ fn assign_value(fields: &[&Field]) -> Vec<TokenStream> {
.collect()
}
-fn gen_read_field(field: &Field, private_ident: &Ident) -> TokenStream {
+pub fn gen_read_field(field: &Field, private_ident: &Ident) -> TokenStream {
let ty = &field.ty;
let base = match classify_trait_object_field(ty) {
StructField::BoxDyn => {
diff --git a/rust/fory-derive/src/object/serializer.rs
b/rust/fory-derive/src/object/serializer.rs
index 29ed1b760..c2e4c6df2 100644
--- a/rust/fory-derive/src/object/serializer.rs
+++ b/rust/fory-derive/src/object/serializer.rs
@@ -309,16 +309,30 @@ fn generate_default_impl(ast: &syn::DeriveInput) ->
proc_macro2::TokenStream {
if !has_default_variant {
if let Some(first_variant) = e.variants.first() {
let variant_ident = &first_variant.ident;
+ let field_defaults = match &first_variant.fields {
+ syn::Fields::Unit => quote! {},
+ syn::Fields::Unnamed(fields) => {
+ let defaults =
+ (0..fields.unnamed.len()).map(|_| quote! {
Default::default() });
+ quote! { (#(#defaults),*) }
+ }
+ syn::Fields::Named(fields) => {
+ let field_idents = fields.named.iter().map(|f|
&f.ident);
+ let defaults =
+ fields.named.iter().map(|_| quote! {
Default::default() });
+ quote! { { #(#field_idents: #defaults),* } }
+ }
+ };
quote! {
impl fory_core::ForyDefault for #name {
fn fory_default() -> Self {
- Self::#variant_ident
+ Self::#variant_ident #field_defaults
}
}
impl std::default::Default for #name {
fn default() -> Self {
- Self::#variant_ident
+ Self::#variant_ident #field_defaults
}
}
}
diff --git a/rust/fory-derive/src/object/write.rs
b/rust/fory-derive/src/object/write.rs
index ae42d8d1e..257b9ec8c 100644
--- a/rust/fory-derive/src/object/write.rs
+++ b/rust/fory-derive/src/object/write.rs
@@ -21,7 +21,7 @@ use super::util::{
should_skip_type_info_for_field, skip_ref_flag, StructField,
};
use fory_core::types::TypeId;
-use proc_macro2::TokenStream;
+use proc_macro2::{Ident, TokenStream};
use quote::quote;
use syn::Field;
@@ -101,13 +101,17 @@ pub fn gen_write_type_info() -> TokenStream {
}
}
-fn gen_write_field(field: &Field) -> TokenStream {
+pub fn gen_write_field(field: &Field, ident: &Ident, use_self: bool) ->
TokenStream {
let ty = &field.ty;
- let ident = &field.ident;
+ let value_ts = if use_self {
+ quote! { self.#ident }
+ } else {
+ quote! { #ident }
+ };
let base = match classify_trait_object_field(ty) {
StructField::BoxDyn => {
quote! {
- <#ty as fory_core::Serializer>::fory_write(&self.#ident,
context, true, true, false)?;
+ <#ty as fory_core::Serializer>::fory_write(&#value_ts,
context, true, true, false)?;
}
}
StructField::RcDyn(trait_name) => {
@@ -115,7 +119,7 @@ fn gen_write_field(field: &Field) -> TokenStream {
let wrapper_ty = types.wrapper_ty;
let trait_ident = types.trait_ident;
quote! {
- let wrapper = #wrapper_ty::from(self.#ident.clone() as
std::rc::Rc<dyn #trait_ident>);
+ let wrapper = #wrapper_ty::from(#value_ts.clone() as
std::rc::Rc<dyn #trait_ident>);
<#wrapper_ty as fory_core::Serializer>::fory_write(&wrapper,
context, true, true, false)?;
}
}
@@ -124,7 +128,7 @@ fn gen_write_field(field: &Field) -> TokenStream {
let wrapper_ty = types.wrapper_ty;
let trait_ident = types.trait_ident;
quote! {
- let wrapper = #wrapper_ty::from(self.#ident.clone() as
std::sync::Arc<dyn #trait_ident>);
+ let wrapper = #wrapper_ty::from(#value_ts.clone() as
std::sync::Arc<dyn #trait_ident>);
<#wrapper_ty as fory_core::Serializer>::fory_write(&wrapper,
context, true, true, false)?;
}
}
@@ -133,7 +137,7 @@ fn gen_write_field(field: &Field) -> TokenStream {
let wrapper_ty = types.wrapper_ty;
let trait_ident = types.trait_ident;
quote! {
- let wrapper_vec: Vec<#wrapper_ty> = self.#ident.iter()
+ let wrapper_vec: Vec<#wrapper_ty> = #value_ts.iter()
.map(|item| #wrapper_ty::from(item.clone() as
std::rc::Rc<dyn #trait_ident>))
.collect();
<Vec<#wrapper_ty> as
fory_core::Serializer>::fory_write(&wrapper_vec, context, true, false, true)?;
@@ -144,7 +148,7 @@ fn gen_write_field(field: &Field) -> TokenStream {
let wrapper_ty = types.wrapper_ty;
let trait_ident = types.trait_ident;
quote! {
- let wrapper_vec: Vec<#wrapper_ty> = self.#ident.iter()
+ let wrapper_vec: Vec<#wrapper_ty> = #value_ts.iter()
.map(|item| #wrapper_ty::from(item.clone() as
std::sync::Arc<dyn #trait_ident>))
.collect();
<Vec<#wrapper_ty> as
fory_core::Serializer>::fory_write(&wrapper_vec, context, true, false, true)?;
@@ -155,7 +159,7 @@ fn gen_write_field(field: &Field) -> TokenStream {
let wrapper_ty = types.wrapper_ty;
let trait_ident = types.trait_ident;
quote! {
- let wrapper_map: std::collections::HashMap<#key_ty,
#wrapper_ty> = self.#ident.iter()
+ let wrapper_map: std::collections::HashMap<#key_ty,
#wrapper_ty> = #value_ts.iter()
.map(|(k, v)| (k.clone(), #wrapper_ty::from(v.clone() as
std::rc::Rc<dyn #trait_ident>)))
.collect();
<std::collections::HashMap<#key_ty, #wrapper_ty> as
fory_core::Serializer>::fory_write(&wrapper_map, context, true, false, true)?;
@@ -166,7 +170,7 @@ fn gen_write_field(field: &Field) -> TokenStream {
let wrapper_ty = types.wrapper_ty;
let trait_ident = types.trait_ident;
quote! {
- let wrapper_map: std::collections::HashMap<#key_ty,
#wrapper_ty> = self.#ident.iter()
+ let wrapper_map: std::collections::HashMap<#key_ty,
#wrapper_ty> = #value_ts.iter()
.map(|(k, v)| (k.clone(), #wrapper_ty::from(v.clone() as
std::sync::Arc<dyn #trait_ident>)))
.collect();
<std::collections::HashMap<#key_ty, #wrapper_ty> as
fory_core::Serializer>::fory_write(&wrapper_map, context, true, false, true)?;
@@ -174,7 +178,7 @@ fn gen_write_field(field: &Field) -> TokenStream {
}
StructField::Forward => {
quote! {
- <#ty as fory_core::Serializer>::fory_write(&self.#ident,
context, true, true, false)?;
+ <#ty as fory_core::Serializer>::fory_write(&#value_ts,
context, true, true, false)?;
}
}
_ => {
@@ -186,7 +190,7 @@ fn gen_write_field(field: &Field) -> TokenStream {
|| type_id == TypeId::MAP as u32
{
quote! {
- <#ty as fory_core::Serializer>::fory_write(&self.#ident,
context, true, false, true)?;
+ <#ty as fory_core::Serializer>::fory_write(&#value_ts,
context, true, false, true)?;
}
} else {
// Known types (primitives, strings, collections) - skip type
info at compile time
@@ -195,29 +199,29 @@ fn gen_write_field(field: &Field) -> TokenStream {
if skip_type_info {
if skip_ref_flag {
quote! {
- <#ty as
fory_core::Serializer>::fory_write_data(&self.#ident, context)?;
+ <#ty as
fory_core::Serializer>::fory_write_data(&#value_ts, context)?;
}
} else {
quote! {
- <#ty as
fory_core::Serializer>::fory_write(&self.#ident, context, true, false, false)?;
+ <#ty as
fory_core::Serializer>::fory_write(&#value_ts, context, true, false, false)?;
}
}
} else if skip_ref_flag {
quote! {
let is_enum = <#ty as
fory_core::Serializer>::fory_static_type_id() == fory_core::types::TypeId::ENUM;
- <#ty as
fory_core::Serializer>::fory_write(&self.#ident, context, false, !is_enum,
false)?;
+ <#ty as fory_core::Serializer>::fory_write(&#value_ts,
context, false, !is_enum, false)?;
}
} else {
quote! {
let is_enum = <#ty as
fory_core::Serializer>::fory_static_type_id() == fory_core::types::TypeId::ENUM;
- <#ty as
fory_core::Serializer>::fory_write(&self.#ident, context, true, !is_enum,
false)?;
+ <#ty as fory_core::Serializer>::fory_write(&#value_ts,
context, true, !is_enum, false)?;
}
}
}
}
};
- if is_debug_enabled() {
+ if is_debug_enabled() && use_self {
let struct_name = get_struct_name().expect("struct context not set");
let struct_name_lit = syn::LitStr::new(&struct_name,
proc_macro2::Span::call_site());
let field_name = field.ident.as_ref().unwrap().to_string();
@@ -243,7 +247,14 @@ fn gen_write_field(field: &Field) -> TokenStream {
}
pub fn gen_write_data(fields: &[&Field]) -> TokenStream {
- let write_fields_ts: Vec<_> = fields.iter().map(|field|
gen_write_field(field)).collect();
+ let write_fields_ts: Vec<_> = fields
+ .iter()
+ .map(|field| {
+ let ident = field.ident.as_ref().unwrap();
+ gen_write_field(field, ident, true)
+ })
+ .collect();
+
let version_hash = compute_struct_version_hash(fields);
quote! {
// Write version hash when class version checking is enabled
diff --git a/rust/tests/tests/test_enum.rs b/rust/tests/tests/test_enum.rs
new file mode 100644
index 000000000..317915860
--- /dev/null
+++ b/rust/tests/tests/test_enum.rs
@@ -0,0 +1,101 @@
+// 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.
+
+// RUSTFLAGS="-Awarnings" cargo expand -p tests --test test_enum
+
+use fory_core::Fory;
+use fory_derive::ForyObject;
+use std::collections::HashMap;
+
+#[test]
+fn basic() {
+ #[derive(ForyObject, Debug, PartialEq)]
+ enum Token {
+ Plus,
+ Number(i64),
+ Ident(String),
+ Assign { target: String, value: i32 },
+ Other(Option<i64>),
+ Child(Box<Token>),
+ Map(HashMap<String, Token>),
+ }
+
+ let mut fory = Fory::default().xlang(false);
+ fory.register::<Token>(1000).unwrap();
+
+ let mut map = HashMap::new();
+ map.insert("one".to_string(), Token::Number(1));
+ map.insert("plus".to_string(), Token::Plus);
+ map.insert(
+ "nested".to_string(),
+ Token::Child(Box::new(Token::Ident("deep".to_string()))),
+ );
+
+ let tokens = vec![
+ Token::Plus,
+ Token::Number(1),
+ Token::Ident("foo".to_string()),
+ Token::Assign {
+ target: "bar".to_string(),
+ value: 42,
+ },
+ Token::Other(Some(42)),
+ Token::Other(None),
+ Token::Child(Box::from(Token::Child(Box::from(Token::Other(None))))),
+ Token::Map(map),
+ ];
+ let bin = fory.serialize(&tokens).unwrap();
+ let new_tokens = fory.deserialize::<Vec<Token>>(&bin).unwrap();
+ assert_eq!(tokens, new_tokens);
+}
+
+#[test]
+fn named_enum() {
+ #[derive(ForyObject, Debug, PartialEq)]
+ enum Token1 {
+ Assign { target: String, value: i32 },
+ }
+
+ #[derive(ForyObject, Debug, PartialEq)]
+ enum Token2 {
+ Assign { value: i32, target: String },
+ }
+
+ let mut fory1 = Fory::default().xlang(false);
+ fory1.register::<Token1>(1000).unwrap();
+
+ let mut fory2 = Fory::default().xlang(false);
+ fory2.register::<Token2>(1000).unwrap();
+
+ let token = Token1::Assign {
+ target: "bar".to_string(),
+ value: 42,
+ };
+ let bin = fory1.serialize(&token).unwrap();
+ let new_token = fory2.deserialize::<Token2>(&bin).unwrap();
+
+ let Token1::Assign {
+ target: target1,
+ value: value1,
+ } = token;
+ let Token2::Assign {
+ target: target2,
+ value: value2,
+ } = new_token;
+ assert_eq!(target1, target2);
+ assert_eq!(value1, value2);
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]