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]

Reply via email to