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 3772f7b73 feat: ✨ support fory skip macro attributes(#2864) (#2865)
3772f7b73 is described below

commit 3772f7b73e752241cee148ff640116168f4e90ae
Author: kitty <[email protected]>
AuthorDate: Sun Nov 2 15:13:06 2025 +0800

    feat: ✨ support fory skip macro attributes(#2864) (#2865)
    
    ## Why?
    
    This PR adds a `#[fory(skip)]` macro attribute similar to
    `#serde[(skip)]` to provide more flexible field skipping capabilities in
    Apache Fory™. This enhancement allows developers to easily mark specific
    fields to be skipped during serialization/deserialization or other
    processing phases, improving code readability and maintainability.
    
    ## What does this PR do?
    
    - Adds a new `#[fory(skip)]` macro attribute implementation
    - The attribute functions similarly to `#[serde(skip)]` but is
    specifically designed for Fory's internal use cases
    - Provides consistent attribute parsing and handling within Fory's macro
    system
    - Maintains backward compatibility with existing code
    
    ## Related issues
    
    - [[Rust] support fory macro attributes
    ](https://github.com/apache/fory/issues/2864)
    
    ## Does this PR introduce any user-facing change?
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    **No**, this PR only adds a new macro attribute that doesn't affect
    existing public APIs or binary protocols. The `#[fory(skip)]` attribute
    is opt-in and doesn't change the behavior of existing code.
    
    ## Benchmark
    
    **No performance impact expected** as this change only adds attribute
    parsing capability without modifying core processing logic. The macro
    attribute is processed at compile time and doesn't introduce runtime
    overhead.
    
    ---------
    
    Co-authored-by: Shawn Yang <[email protected]>
---
 rust/fory-derive/src/lib.rs                |   2 +-
 rust/fory-derive/src/object/derive_enum.rs |  31 ++-
 rust/fory-derive/src/object/misc.rs        |   6 +-
 rust/fory-derive/src/object/read.rs        | 251 ++++++++++-------
 rust/fory-derive/src/object/util.rs        |  45 ++-
 rust/fory-derive/src/object/write.rs       |   7 +-
 rust/tests/tests/test_skip_fields.rs       | 433 +++++++++++++++++++++++++++++
 7 files changed, 654 insertions(+), 121 deletions(-)

diff --git a/rust/fory-derive/src/lib.rs b/rust/fory-derive/src/lib.rs
index 0ef903e71..49901b8d6 100644
--- a/rust/fory-derive/src/lib.rs
+++ b/rust/fory-derive/src/lib.rs
@@ -198,7 +198,7 @@ mod util;
 ///     city: String,
 /// }
 /// ```
-#[proc_macro_derive(ForyObject, attributes(fory_debug))]
+#[proc_macro_derive(ForyObject, attributes(fory_debug, fory))]
 pub fn proc_macro_derive_fory_object(input: proc_macro::TokenStream) -> 
TokenStream {
     let input = parse_macro_input!(input as DeriveInput);
 
diff --git a/rust/fory-derive/src/object/derive_enum.rs 
b/rust/fory-derive/src/object/derive_enum.rs
index c7a96c422..93462e2ca 100644
--- a/rust/fory-derive/src/object/derive_enum.rs
+++ b/rust/fory-derive/src/object/derive_enum.rs
@@ -15,6 +15,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
+use super::util::{is_default_value_variant, is_skip_enum_variant};
 use crate::object::read::gen_read_field;
 
 use crate::object::write::gen_write_field;
@@ -51,13 +52,21 @@ pub fn gen_write(_data_enum: &DataEnum) -> TokenStream {
 }
 
 pub fn gen_write_data(data_enum: &DataEnum) -> TokenStream {
+    let default_variant_value = data_enum
+        .variants
+        .iter()
+        .position(is_default_value_variant)
+        .unwrap_or(0) as u32;
     let xlang_variant_branches: Vec<TokenStream> = data_enum
         .variants
         .iter()
         .enumerate()
         .map(|(idx, v)| {
             let ident = &v.ident;
-            let tag_value = idx as u32;
+            let mut tag_value = idx as u32;
+            if is_skip_enum_variant(v) {
+                tag_value = default_variant_value;
+            }
 
             match &v.fields {
                 Fields::Unit => {
@@ -91,7 +100,10 @@ pub fn gen_write_data(data_enum: &DataEnum) -> TokenStream {
         .enumerate()
         .map(|(idx, v)| {
             let ident = &v.ident;
-            let tag_value = idx as u32;
+            let mut tag_value = idx as u32;
+            if is_skip_enum_variant(v) {
+                tag_value = default_variant_value;
+            }
 
             match &v.fields {
                 Fields::Unit => {
@@ -186,13 +198,21 @@ pub fn gen_read_with_type_info(_: &DataEnum) -> 
TokenStream {
 }
 
 pub fn gen_read_data(data_enum: &DataEnum) -> TokenStream {
+    let default_variant_value = data_enum
+        .variants
+        .iter()
+        .position(is_default_value_variant)
+        .unwrap_or(0) as u32;
     let xlang_variant_branches: Vec<TokenStream> = data_enum
         .variants
         .iter()
         .enumerate()
         .map(|(idx, v)| {
             let ident = &v.ident;
-            let tag_value = idx as u32;
+            let mut tag_value = idx as u32;
+            if is_skip_enum_variant(v) {
+                tag_value = default_variant_value;
+            }
 
             match &v.fields {
                 Fields::Unit => {
@@ -237,7 +257,10 @@ pub fn gen_read_data(data_enum: &DataEnum) -> TokenStream {
         .enumerate()
         .map(|(idx, v)| {
             let ident = &v.ident;
-            let tag_value = idx as u32;
+            let mut tag_value = idx as u32;
+            if is_skip_enum_variant(v) {
+                tag_value = default_variant_value;
+            }
 
             match &v.fields {
                 Fields::Unit => {
diff --git a/rust/fory-derive/src/object/misc.rs 
b/rust/fory-derive/src/object/misc.rs
index f122198c8..1e35ebafa 100644
--- a/rust/fory-derive/src/object/misc.rs
+++ b/rust/fory-derive/src/object/misc.rs
@@ -21,8 +21,8 @@ use std::sync::atomic::{AtomicU32, Ordering};
 use syn::Field;
 
 use super::util::{
-    classify_trait_object_field, generic_tree_to_tokens, get_sort_fields_ts, 
parse_generic_tree,
-    StructField,
+    classify_trait_object_field, generic_tree_to_tokens, 
get_filtered_fields_iter,
+    get_sort_fields_ts, parse_generic_tree, StructField,
 };
 
 // Global type ID counter that auto-grows from 0 at macro processing time
@@ -72,7 +72,7 @@ pub fn gen_get_sorted_field_names(fields: &[&Field]) -> 
TokenStream {
 }
 
 pub fn gen_field_fields_info(fields: &[&Field]) -> TokenStream {
-    let field_infos = fields.iter().map(|field| {
+    let field_infos = get_filtered_fields_iter(fields).map(|field| {
         let ty = &field.ty;
         let name = format!("{}", field.ident.as_ref().expect("should be field 
name"));
         match classify_trait_object_field(ty) {
diff --git a/rust/fory-derive/src/object/read.rs 
b/rust/fory-derive/src/object/read.rs
index 1bdb8eb11..fc8573173 100644
--- a/rust/fory-derive/src/object/read.rs
+++ b/rust/fory-derive/src/object/read.rs
@@ -22,7 +22,7 @@ use syn::{Field, Type};
 use super::util::{
     classify_trait_object_field, compute_struct_version_hash, 
create_wrapper_types_arc,
     create_wrapper_types_rc, extract_type_name, get_struct_name, 
is_debug_enabled,
-    is_primitive_type, should_skip_type_info_for_field, skip_ref_flag, 
StructField,
+    is_primitive_type, is_skip_field, should_skip_type_info_for_field, 
skip_ref_flag, StructField,
 };
 
 fn create_private_field_name(field: &Field) -> Ident {
@@ -103,6 +103,12 @@ fn assign_value(fields: &[&Field]) -> Vec<TokenStream> {
 
 pub fn gen_read_field(field: &Field, private_ident: &Ident) -> TokenStream {
     let ty = &field.ty;
+    if is_skip_field(field) {
+        return quote! {
+            let #private_ident = <#ty as 
fory_core::ForyDefault>::fory_default();
+        };
+    }
+
     let base = match classify_trait_object_field(ty) {
         StructField::BoxDyn => {
             quote! {
@@ -277,146 +283,175 @@ pub fn gen_read_data(fields: &[&Field]) -> TokenStream {
 fn gen_read_compatible_match_arm_body(field: &Field, var_name: &Ident) -> 
TokenStream {
     let ty = &field.ty;
     let field_kind = classify_trait_object_field(ty);
+    let is_skip_flag = is_skip_field(field);
 
-    let base = match field_kind {
-        StructField::BoxDyn => {
-            quote! {
-                #var_name = Some(<#ty as 
fory_core::Serializer>::fory_read(context, true, true)?);
+    let base = if is_skip_flag {
+        match field_kind {
+            StructField::None => {
+                // Note: _base_ty is currently unused but kept for potential 
future use
+                let _base_ty = match &ty {
+                    Type::Path(type_path) => 
Some(&type_path.path.segments.first().unwrap().ident),
+                    Type::Tuple(_) => None, // Tuples don't have a simple ident
+                    _ => None,              // Other types also don't have a 
simple ident
+                };
+                let dec_by_option = need_declared_by_option(field);
+                if dec_by_option {
+                    quote! {
+                        #var_name = Some(<#ty as 
fory_core::ForyDefault>::fory_default());
+                    }
+                } else {
+                    quote! {
+                        #var_name = <#ty as 
fory_core::ForyDefault>::fory_default();
+                    }
+                }
             }
-        }
-        StructField::RcDyn(trait_name) => {
-            let types = create_wrapper_types_rc(&trait_name);
-            let wrapper_ty = types.wrapper_ty;
-            let trait_ident = types.trait_ident;
-            quote! {
-                let wrapper = <#wrapper_ty as 
fory_core::Serializer>::fory_read(context, true, true)?;
-                #var_name = Some(std::rc::Rc::<dyn 
#trait_ident>::from(wrapper));
+            _ => {
+                quote! {
+                    #var_name = Some(<#ty as 
fory_core::ForyDefault>::fory_default());
+                }
             }
         }
-        StructField::ArcDyn(trait_name) => {
-            let types = create_wrapper_types_arc(&trait_name);
-            let wrapper_ty = types.wrapper_ty;
-            let trait_ident = types.trait_ident;
-            quote! {
-                let wrapper = <#wrapper_ty as 
fory_core::Serializer>::fory_read(context, true, true)?;
-                #var_name = Some(std::sync::Arc::<dyn 
#trait_ident>::from(wrapper));
+    } else {
+        match field_kind {
+            StructField::BoxDyn => {
+                quote! {
+                    #var_name = Some(<#ty as 
fory_core::Serializer>::fory_read(context, true, true)?);
+                }
             }
-        }
-        StructField::VecRc(trait_name) => {
-            let types = create_wrapper_types_rc(&trait_name);
-            let wrapper_ty = types.wrapper_ty;
-            let trait_ident = types.trait_ident;
-            quote! {
-                let wrapper_vec = <Vec<#wrapper_ty> as 
fory_core::Serializer>::fory_read(context, true, false)?;
-                #var_name = Some(wrapper_vec.into_iter()
-                    .map(|w| std::rc::Rc::<dyn #trait_ident>::from(w))
-                    .collect());
+            StructField::RcDyn(trait_name) => {
+                let types = create_wrapper_types_rc(&trait_name);
+                let wrapper_ty = types.wrapper_ty;
+                let trait_ident = types.trait_ident;
+                quote! {
+                    let wrapper = <#wrapper_ty as 
fory_core::Serializer>::fory_read(context, true, true)?;
+                    #var_name = Some(std::rc::Rc::<dyn 
#trait_ident>::from(wrapper));
+                }
             }
-        }
-        StructField::VecArc(trait_name) => {
-            let types = create_wrapper_types_arc(&trait_name);
-            let wrapper_ty = types.wrapper_ty;
-            let trait_ident = types.trait_ident;
-            quote! {
-                let wrapper_vec = <Vec<#wrapper_ty> as 
fory_core::Serializer>::fory_read(context, true, false)?;
-                #var_name = Some(wrapper_vec.into_iter()
-                    .map(|w| std::sync::Arc::<dyn #trait_ident>::from(w))
-                    .collect());
+            StructField::ArcDyn(trait_name) => {
+                let types = create_wrapper_types_arc(&trait_name);
+                let wrapper_ty = types.wrapper_ty;
+                let trait_ident = types.trait_ident;
+                quote! {
+                    let wrapper = <#wrapper_ty as 
fory_core::Serializer>::fory_read(context, true, true)?;
+                    #var_name = Some(std::sync::Arc::<dyn 
#trait_ident>::from(wrapper));
+                }
             }
-        }
-        StructField::HashMapRc(key_ty, trait_name) => {
-            let types = create_wrapper_types_rc(&trait_name);
-            let wrapper_ty = types.wrapper_ty;
-            let trait_ident = types.trait_ident;
-            quote! {
-                let wrapper_map = <std::collections::HashMap<#key_ty, 
#wrapper_ty> as fory_core::Serializer>::fory_read(context, true, false)?;
-                #var_name = Some(wrapper_map.into_iter()
-                    .map(|(k, v)| (k, std::rc::Rc::<dyn 
#trait_ident>::from(v)))
-                    .collect());
+            StructField::VecRc(trait_name) => {
+                let types = create_wrapper_types_rc(&trait_name);
+                let wrapper_ty = types.wrapper_ty;
+                let trait_ident = types.trait_ident;
+                quote! {
+                    let wrapper_vec = <Vec<#wrapper_ty> as 
fory_core::Serializer>::fory_read(context, true, false)?;
+                    #var_name = Some(wrapper_vec.into_iter()
+                        .map(|w| std::rc::Rc::<dyn #trait_ident>::from(w))
+                        .collect());
+                }
             }
-        }
-        StructField::HashMapArc(key_ty, trait_name) => {
-            let types = create_wrapper_types_arc(&trait_name);
-            let wrapper_ty = types.wrapper_ty;
-            let trait_ident = types.trait_ident;
-            quote! {
-                let wrapper_map = <std::collections::HashMap<#key_ty, 
#wrapper_ty> as fory_core::Serializer>::fory_read(context, true, false)?;
-                #var_name = Some(wrapper_map.into_iter()
-                    .map(|(k, v)| (k, std::sync::Arc::<dyn 
#trait_ident>::from(v)))
-                    .collect());
+            StructField::VecArc(trait_name) => {
+                let types = create_wrapper_types_arc(&trait_name);
+                let wrapper_ty = types.wrapper_ty;
+                let trait_ident = types.trait_ident;
+                quote! {
+                    let wrapper_vec = <Vec<#wrapper_ty> as 
fory_core::Serializer>::fory_read(context, true, false)?;
+                    #var_name = Some(wrapper_vec.into_iter()
+                        .map(|w| std::sync::Arc::<dyn #trait_ident>::from(w))
+                        .collect());
+                }
             }
-        }
-        StructField::ContainsTraitObject => {
-            quote! {
-                #var_name = Some(<#ty as 
fory_core::Serializer>::fory_read(context, true, true)?);
+            StructField::HashMapRc(key_ty, trait_name) => {
+                let types = create_wrapper_types_rc(&trait_name);
+                let wrapper_ty = types.wrapper_ty;
+                let trait_ident = types.trait_ident;
+                quote! {
+                    let wrapper_map = <std::collections::HashMap<#key_ty, 
#wrapper_ty> as fory_core::Serializer>::fory_read(context, true, false)?;
+                    #var_name = Some(wrapper_map.into_iter()
+                        .map(|(k, v)| (k, std::rc::Rc::<dyn 
#trait_ident>::from(v)))
+                        .collect());
+                }
             }
-        }
-        StructField::Forward => {
-            quote! {
-                #var_name = Some(<#ty as 
fory_core::Serializer>::fory_read(context, true, true)?);
+            StructField::HashMapArc(key_ty, trait_name) => {
+                let types = create_wrapper_types_arc(&trait_name);
+                let wrapper_ty = types.wrapper_ty;
+                let trait_ident = types.trait_ident;
+                quote! {
+                    let wrapper_map = <std::collections::HashMap<#key_ty, 
#wrapper_ty> as fory_core::Serializer>::fory_read(context, true, false)?;
+                    #var_name = Some(wrapper_map.into_iter()
+                        .map(|(k, v)| (k, std::sync::Arc::<dyn 
#trait_ident>::from(v)))
+                        .collect());
+                }
             }
-        }
-        StructField::None => {
-            // Note: _base_ty is currently unused but kept for potential 
future use
-            let _base_ty = match &ty {
-                Type::Path(type_path) => 
Some(&type_path.path.segments.first().unwrap().ident),
-                Type::Tuple(_) => None, // Tuples don't have a simple ident
-                _ => None,              // Other types also don't have a 
simple ident
-            };
-            let skip_type_info = should_skip_type_info_for_field(ty);
-            let dec_by_option = need_declared_by_option(field);
-            if skip_type_info {
-                if dec_by_option {
+            StructField::ContainsTraitObject => {
+                quote! {
+                    #var_name = Some(<#ty as 
fory_core::Serializer>::fory_read(context, true, true)?);
+                }
+            }
+            StructField::Forward => {
+                quote! {
+                    #var_name = Some(<#ty as 
fory_core::Serializer>::fory_read(context, true, true)?);
+                }
+            }
+            StructField::None => {
+                // Note: _base_ty is currently unused but kept for potential 
future use
+                let _base_ty = match &ty {
+                    Type::Path(type_path) => 
Some(&type_path.path.segments.first().unwrap().ident),
+                    Type::Tuple(_) => None, // Tuples don't have a simple ident
+                    _ => None,              // Other types also don't have a 
simple ident
+                };
+                let skip_type_info = should_skip_type_info_for_field(ty);
+                let dec_by_option = need_declared_by_option(field);
+                if skip_type_info {
+                    if dec_by_option {
+                        quote! {
+                            let read_ref_flag = 
fory_core::serializer::util::field_need_write_ref_into(
+                                _field.field_type.type_id,
+                                _field.field_type.nullable,
+                            );
+                            if read_ref_flag {
+                                #var_name = Some(<#ty as 
fory_core::Serializer>::fory_read(context, true, false)?);
+                            } else {
+                                #var_name = Some(<#ty as 
fory_core::Serializer>::fory_read_data(context)?);
+                            }
+                        }
+                    } else {
+                        quote! {
+                            let read_ref_flag = 
fory_core::serializer::util::field_need_write_ref_into(
+                                _field.field_type.type_id,
+                                _field.field_type.nullable,
+                            );
+                            if read_ref_flag {
+                                #var_name = <#ty as 
fory_core::Serializer>::fory_read(context, true, false)?;
+                            } else {
+                                #var_name = <#ty as 
fory_core::Serializer>::fory_read_data(context)?;
+                            }
+                        }
+                    }
+                } else if dec_by_option {
                     quote! {
+                        let read_type_info = 
fory_core::serializer::util::field_need_read_type_info(_field.field_type.type_id);
                         let read_ref_flag = 
fory_core::serializer::util::field_need_write_ref_into(
                             _field.field_type.type_id,
                             _field.field_type.nullable,
                         );
                         if read_ref_flag {
-                            #var_name = Some(<#ty as 
fory_core::Serializer>::fory_read(context, true, false)?);
+                            #var_name = Some(<#ty as 
fory_core::Serializer>::fory_read(context, true, read_type_info)?);
                         } else {
                             #var_name = Some(<#ty as 
fory_core::Serializer>::fory_read_data(context)?);
                         }
                     }
                 } else {
                     quote! {
+                        let read_type_info = 
fory_core::serializer::util::field_need_read_type_info(_field.field_type.type_id);
                         let read_ref_flag = 
fory_core::serializer::util::field_need_write_ref_into(
                             _field.field_type.type_id,
                             _field.field_type.nullable,
                         );
                         if read_ref_flag {
-                            #var_name = <#ty as 
fory_core::Serializer>::fory_read(context, true, false)?;
+                            #var_name = <#ty as 
fory_core::Serializer>::fory_read(context, true, read_type_info)?;
                         } else {
                             #var_name = <#ty as 
fory_core::Serializer>::fory_read_data(context)?;
                         }
                     }
                 }
-            } else if dec_by_option {
-                quote! {
-                    let read_type_info = 
fory_core::serializer::util::field_need_read_type_info(_field.field_type.type_id);
-                    let read_ref_flag = 
fory_core::serializer::util::field_need_write_ref_into(
-                        _field.field_type.type_id,
-                        _field.field_type.nullable,
-                    );
-                    if read_ref_flag {
-                        #var_name = Some(<#ty as 
fory_core::Serializer>::fory_read(context, true, read_type_info)?);
-                    } else {
-                        #var_name = Some(<#ty as 
fory_core::Serializer>::fory_read_data(context)?);
-                    }
-                }
-            } else {
-                quote! {
-                    let read_type_info = 
fory_core::serializer::util::field_need_read_type_info(_field.field_type.type_id);
-                    let read_ref_flag = 
fory_core::serializer::util::field_need_write_ref_into(
-                        _field.field_type.type_id,
-                        _field.field_type.nullable,
-                    );
-                    if read_ref_flag {
-                        #var_name = <#ty as 
fory_core::Serializer>::fory_read(context, true, read_type_info)?;
-                    } else {
-                        #var_name = <#ty as 
fory_core::Serializer>::fory_read_data(context)?;
-                    }
-                }
             }
         }
     };
diff --git a/rust/fory-derive/src/object/util.rs 
b/rust/fory-derive/src/object/util.rs
index 50def2ca0..92e184ced 100644
--- a/rust/fory-derive/src/object/util.rs
+++ b/rust/fory-derive/src/object/util.rs
@@ -767,8 +767,14 @@ pub(crate) fn get_sorted_field_names(fields: &[&Field]) -> 
Vec<String> {
     all_fields.into_iter().map(|(name, _, _)| name).collect()
 }
 
+pub(crate) fn get_filtered_fields_iter<'a>(
+    fields: &'a [&'a Field],
+) -> impl Iterator<Item = &'a Field> {
+    fields.iter().filter(|field| !is_skip_field(field)).copied()
+}
 pub(super) fn get_sort_fields_ts(fields: &[&Field]) -> TokenStream {
-    let sorted_names = get_sorted_field_names(fields);
+    let filterd_fields: Vec<&Field> = 
get_filtered_fields_iter(fields).collect();
+    let sorted_names = get_sorted_field_names(&filterd_fields);
     let names = sorted_names.iter().map(|name| {
         quote! { #name }
     });
@@ -898,6 +904,43 @@ pub(crate) fn should_skip_type_info_for_field(ty: &Type) 
-> bool {
     true
 }
 
+pub(crate) fn is_skip_field(field: &syn::Field) -> bool {
+    field.attrs.iter().any(|attr| {
+        attr.path().is_ident("fory") && {
+            let mut skip = false;
+            let _ = attr.parse_nested_meta(|meta| {
+                if meta.path.is_ident("skip") {
+                    skip = true;
+                }
+                Ok(())
+            });
+            skip
+        }
+    })
+}
+
+pub(crate) fn is_skip_enum_variant(variant: &syn::Variant) -> bool {
+    variant.attrs.iter().any(|attr| {
+        attr.path().is_ident("fory") && {
+            let mut skip = false;
+            let _ = attr.parse_nested_meta(|meta| {
+                if meta.path.is_ident("skip") {
+                    skip = true;
+                }
+                Ok(())
+            });
+            skip
+        }
+    })
+}
+
+pub(crate) fn is_default_value_variant(variant: &syn::Variant) -> bool {
+    variant
+        .attrs
+        .iter()
+        .any(|attr| attr.path().is_ident("default"))
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/rust/fory-derive/src/object/write.rs 
b/rust/fory-derive/src/object/write.rs
index fba08ced4..4c1b505bc 100644
--- a/rust/fory-derive/src/object/write.rs
+++ b/rust/fory-derive/src/object/write.rs
@@ -17,8 +17,8 @@
 
 use super::util::{
     classify_trait_object_field, compute_struct_version_hash, 
create_wrapper_types_arc,
-    create_wrapper_types_rc, get_struct_name, get_type_id_by_type_ast, 
is_debug_enabled,
-    should_skip_type_info_for_field, skip_ref_flag, StructField,
+    create_wrapper_types_rc, get_filtered_fields_iter, get_struct_name, 
get_type_id_by_type_ast,
+    is_debug_enabled, should_skip_type_info_for_field, skip_ref_flag, 
StructField,
 };
 use fory_core::types::TypeId;
 use proc_macro2::{Ident, TokenStream};
@@ -247,8 +247,7 @@ pub fn gen_write_field(field: &Field, ident: &Ident, 
use_self: bool) -> TokenStr
 }
 
 pub fn gen_write_data(fields: &[&Field]) -> TokenStream {
-    let write_fields_ts: Vec<_> = fields
-        .iter()
+    let write_fields_ts: Vec<_> = get_filtered_fields_iter(fields)
         .map(|field| {
             let ident = field.ident.as_ref().unwrap();
             gen_write_field(field, ident, true)
diff --git a/rust/tests/tests/test_skip_fields.rs 
b/rust/tests/tests/test_skip_fields.rs
new file mode 100644
index 000000000..00e8e4949
--- /dev/null
+++ b/rust/tests/tests/test_skip_fields.rs
@@ -0,0 +1,433 @@
+// 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 fory_core::{Fory, ForyDefault, Reader};
+use fory_derive::ForyObject;
+
+#[derive(ForyObject, Debug, PartialEq)]
+struct TestSkipFields {
+    serialized_field: i32,
+    #[fory(skip)]
+    skipped_field: String,
+    another_serialized: f64,
+}
+
+#[derive(ForyObject, Debug, PartialEq)]
+struct NestedStruct {
+    value: i32,
+}
+
+#[derive(ForyObject, Debug, PartialEq)]
+struct TestNestedSkip {
+    normal_field: i32,
+    nested: NestedStruct,
+    #[fory(skip)]
+    skipped_nested: NestedStruct,
+}
+
+#[derive(ForyObject, Debug, PartialEq)]
+struct MultipleSkipFields {
+    field1: i32,
+    #[fory(skip)]
+    skipped1: String,
+    field2: f64,
+    #[fory(skip)]
+    skipped2: bool,
+    field3: f32,
+}
+
+#[derive(ForyObject, Debug, PartialEq)]
+struct AllFieldsSkipped {
+    #[fory(skip)]
+    skipped1: String,
+    #[fory(skip)]
+    skipped2: i32,
+    #[fory(skip)]
+    skipped3: f64,
+}
+
+#[derive(ForyObject, Debug, PartialEq)]
+struct ComplexNestedSkip {
+    normal_field: i32,
+    #[fory(skip)]
+    skipped_field: String,
+    nested: TestSkipFields,
+    #[fory(skip)]
+    skipped_nested: TestSkipFields,
+}
+
+#[derive(ForyObject, Debug, PartialEq)]
+enum TestEnumSkip {
+    Pending,
+    // #[default]
+    Active,
+    Inactive,
+    #[fory(skip)]
+    Deleted,
+}
+
+#[test]
+fn test_basic_skip_functionality() {
+    let mut fory = Fory::default();
+    fory.register::<TestSkipFields>(1).unwrap();
+
+    let original = TestSkipFields {
+        serialized_field: 42,
+        skipped_field: "this should be skipped".to_string(),
+        another_serialized: 2.142,
+    };
+
+    let bytes = fory.serialize(&original).unwrap();
+    let decoded: TestSkipFields = fory.deserialize(&bytes).unwrap();
+    assert_eq!(original.serialized_field, decoded.serialized_field);
+    assert_eq!(original.another_serialized, decoded.another_serialized);
+    assert_eq!(decoded.skipped_field, String::default());
+
+    let mut buf: Vec<u8> = vec![];
+    fory.serialize_to(&original, &mut buf).unwrap();
+    let mut reader = Reader::new(&buf);
+    let decoded: TestSkipFields = fory.deserialize_from(&mut reader).unwrap();
+    assert_eq!(original.serialized_field, decoded.serialized_field);
+    assert_eq!(original.another_serialized, decoded.another_serialized);
+    assert_eq!(decoded.skipped_field, String::default());
+}
+
+#[test]
+fn test_nested_skip_functionality() {
+    let mut fory = Fory::default();
+    fory.register::<TestNestedSkip>(2).unwrap();
+    fory.register::<NestedStruct>(3).unwrap();
+
+    let original = TestNestedSkip {
+        normal_field: 100,
+        nested: NestedStruct { value: 200 },
+        skipped_nested: NestedStruct { value: 300 },
+    };
+
+    let bytes = fory.serialize(&original).unwrap();
+    let decoded: TestNestedSkip = fory.deserialize(&bytes).unwrap();
+
+    assert_eq!(original.normal_field, decoded.normal_field);
+    assert_eq!(original.nested, decoded.nested);
+    assert_eq!(decoded.skipped_nested, NestedStruct::default());
+}
+
+#[test]
+fn test_multiple_skip_fields() {
+    let mut fory = Fory::default();
+    fory.register::<MultipleSkipFields>(3).unwrap();
+
+    let original = MultipleSkipFields {
+        field1: 42,
+        skipped1: "skipped string".to_string(),
+        field2: 2.71,
+        skipped2: true,
+        field3: 255.9,
+    };
+
+    let bytes = fory.serialize(&original).unwrap();
+    let decoded: MultipleSkipFields = fory.deserialize(&bytes).unwrap();
+
+    assert_eq!(original.field1, decoded.field1);
+    assert_eq!(original.field2, decoded.field2);
+    assert_eq!(original.field3, decoded.field3);
+    assert_eq!(decoded.skipped1, String::default());
+    assert_eq!(decoded.skipped2, bool::default());
+}
+
+#[test]
+fn test_all_fields_skipped() {
+    let mut fory = Fory::default();
+    fory.register::<AllFieldsSkipped>(4).unwrap();
+
+    let original = AllFieldsSkipped {
+        skipped1: "test1".to_string(),
+        skipped2: 42,
+        skipped3: 2.142,
+    };
+
+    let bytes = fory.serialize(&original).unwrap();
+    let decoded: AllFieldsSkipped = fory.deserialize(&bytes).unwrap();
+
+    assert_eq!(decoded.skipped1, String::default());
+    assert_eq!(decoded.skipped2, i32::default());
+    assert_eq!(decoded.skipped3, f64::default());
+}
+
+#[test]
+fn test_complex_nested_skip() {
+    let mut fory = Fory::default();
+    fory.register::<ComplexNestedSkip>(5).unwrap();
+    fory.register::<TestSkipFields>(6).unwrap();
+
+    let original = ComplexNestedSkip {
+        normal_field: 1,
+        skipped_field: "should be skipped".to_string(),
+        nested: TestSkipFields {
+            serialized_field: 2,
+            skipped_field: "nested skipped".to_string(),
+            another_serialized: 1.41,
+        },
+        skipped_nested: TestSkipFields {
+            serialized_field: 3,
+            skipped_field: "completely skipped".to_string(),
+            another_serialized: 2.71,
+        },
+    };
+
+    let bytes = fory.serialize(&original).unwrap();
+    let decoded: ComplexNestedSkip = fory.deserialize(&bytes).unwrap();
+
+    assert_eq!(original.normal_field, decoded.normal_field);
+    assert_eq!(
+        original.nested.serialized_field,
+        decoded.nested.serialized_field
+    );
+    assert_eq!(decoded.nested.skipped_field, String::default());
+    assert_eq!(decoded.skipped_field, String::default());
+    assert_eq!(decoded.skipped_nested, TestSkipFields::default());
+}
+
+#[test]
+fn test_enum_skip() {
+    let mut fory = Fory::default();
+    fory.register::<TestEnumSkip>(6).unwrap();
+
+    let original_v1 = TestEnumSkip::Pending;
+
+    let bytes = fory.serialize(&original_v1).unwrap();
+    let decoded: TestEnumSkip = fory.deserialize(&bytes).unwrap();
+    assert_eq!(original_v1, decoded);
+
+    let original_skip = TestEnumSkip::Deleted;
+    let bytes = fory.serialize(&original_skip).unwrap();
+    let decoded: TestEnumSkip = fory.deserialize(&bytes).unwrap();
+    assert_eq!(decoded, TestEnumSkip::default());
+}
+
+#[test]
+fn test_skip_serialization_size() {
+    let mut fory = Fory::default();
+    fory.register::<TestSkipFields>(10).unwrap();
+
+    let with_skip = TestSkipFields {
+        serialized_field: 42,
+        skipped_field: "this is a long string that should be 
skipped".to_string(),
+        another_serialized: 2.142,
+    };
+    #[derive(ForyObject, Debug, PartialEq)]
+    struct TestNoSkip {
+        serialized_field: i32,
+        skipped_field: String,
+        another_serialized: f64,
+    }
+
+    fory.register::<TestNoSkip>(11).unwrap();
+
+    let without_skip = TestNoSkip {
+        serialized_field: 42,
+        skipped_field: "this is a long string that should be 
skipped".to_string(),
+        another_serialized: 2.142,
+    };
+
+    let bytes_with_skip = fory.serialize(&with_skip).unwrap();
+    let bytes_without_skip = fory.serialize(&without_skip).unwrap();
+
+    assert!(
+        bytes_with_skip.len() < bytes_without_skip.len(),
+        "Skipped version should be smaller: {} < {}",
+        bytes_with_skip.len(),
+        bytes_without_skip.len()
+    );
+}
+
+#[test]
+fn test_skip_with_different_types() {
+    #[derive(ForyObject, Debug, PartialEq)]
+    struct MultiTypeSkip {
+        field1: i32,
+        #[fory(skip)]
+        skipped_string: String,
+        field2: f64,
+        #[fory(skip)]
+        skipped_bool: bool,
+        field3: i8,
+        #[fory(skip)]
+        skipped_vec: Vec<i32>,
+        field4: i64,
+    }
+
+    let mut fory = Fory::default();
+    fory.register::<MultiTypeSkip>(12).unwrap();
+
+    let original = MultiTypeSkip {
+        field1: 1,
+        skipped_string: "test".to_string(),
+        field2: 2.0,
+        skipped_bool: true,
+        field3: 3,
+        skipped_vec: vec![1, 2, 3],
+        field4: 4,
+    };
+    let bytes = fory.serialize(&original).unwrap();
+    let decoded: MultiTypeSkip = fory.deserialize(&bytes).unwrap();
+
+    assert_eq!(original.field1, decoded.field1);
+    assert_eq!(original.field2, decoded.field2);
+    assert_eq!(original.field3, decoded.field3);
+    assert_eq!(original.field4, decoded.field4);
+
+    assert_eq!(decoded.skipped_string, String::default());
+    assert_eq!(decoded.skipped_bool, bool::default());
+    assert_eq!(decoded.skipped_vec, Vec::<i32>::default());
+}
+
+#[test]
+fn test_trait_object_serialization() {
+    use fory_core::ForyDefault;
+    use fory_core::Serializer;
+    use fory_core::{register_trait_type, Fory};
+    use fory_derive::ForyObject;
+    use std::collections::HashMap;
+    use std::rc::Rc;
+    use std::sync::Arc;
+
+    trait Animal: Serializer {
+        fn speak(&self) -> String;
+        fn name(&self) -> &str;
+    }
+
+    #[derive(ForyObject, Debug)]
+    struct Dog {
+        name: String,
+        breed: String,
+    }
+
+    impl Animal for Dog {
+        fn speak(&self) -> String {
+            "Woof!".to_string()
+        }
+        fn name(&self) -> &str {
+            &self.name
+        }
+    }
+
+    #[derive(ForyObject, Debug)]
+    struct Cat {
+        name: String,
+        color: String,
+    }
+
+    impl Animal for Cat {
+        fn speak(&self) -> String {
+            "Meow!".to_string()
+        }
+        fn name(&self) -> &str {
+            &self.name
+        }
+    }
+
+    register_trait_type!(Animal, Dog, Cat);
+
+    #[derive(ForyObject)]
+    struct Zoo {
+        star_animal: Box<dyn Animal>,
+    }
+
+    #[derive(ForyObject)]
+    struct ZooWithSkip {
+        regular_animal: Box<dyn Animal>,
+        #[fory(skip)]
+        skipped_animal: Box<dyn Animal>,
+    }
+
+    let mut fory = Fory::default().compatible(true);
+    fory.register::<Dog>(100).unwrap();
+    fory.register::<Cat>(101).unwrap();
+    fory.register::<Zoo>(102).unwrap();
+    fory.register::<ZooWithSkip>(103).unwrap();
+
+    let zoo_with_skip = ZooWithSkip {
+        regular_animal: Box::new(Dog {
+            name: "Speedy".to_string(),
+            breed: "Greyhound".to_string(),
+        }),
+        skipped_animal: Box::new(Cat {
+            name: "Felix".to_string(),
+            color: "Black".to_string(),
+        }),
+    };
+
+    let bytes_skip = fory.serialize(&zoo_with_skip).unwrap();
+
+    let decoded_skip: ZooWithSkip = fory.deserialize(&bytes_skip).unwrap();
+
+    assert_eq!(decoded_skip.regular_animal.name(), "Speedy");
+    assert_eq!(decoded_skip.regular_animal.speak(), "Woof!");
+
+    assert_eq!(decoded_skip.skipped_animal.name(), "".to_string());
+    assert_eq!(decoded_skip.skipped_animal.speak(), "Woof!".to_string());
+
+    #[derive(ForyObject)]
+    struct ComplexSkipExample {
+        #[fory(skip)]
+        boxed_dyn: Box<dyn Animal>,
+        #[fory(skip)]
+        animals_arc: Vec<Arc<dyn Animal>>,
+        #[fory(skip)]
+        registry: HashMap<String, Arc<dyn Animal>>,
+        #[fory(skip)]
+        animals_rc: Vec<Rc<dyn Animal>>,
+        normal_field: String,
+    }
+
+    let mut fory = Fory::default().compatible(true);
+    fory.register::<ComplexSkipExample>(106).unwrap();
+
+    let complex = ComplexSkipExample {
+        boxed_dyn: Box::new(Dog {
+            name: "BoxTest".to_string(),
+            breed: "BoxTestBreed".to_string(),
+        }) as Box<dyn Animal>,
+        animals_rc: vec![Rc::new(Cat {
+            name: "RcTest".to_string(),
+            color: "RcTestColor".to_string(),
+        }) as Rc<dyn Animal>],
+        animals_arc: vec![Arc::new(Cat {
+            name: "RcTest".to_string(),
+            color: "RcTestColor".to_string(),
+        }) as Arc<dyn Animal>],
+        registry: HashMap::from_iter([(
+            "arc_map".to_string(),
+            Arc::new(Dog {
+                name: "ArcTest".to_string(),
+                breed: "ArcTestBreed".to_string(),
+            }) as Arc<dyn Animal>,
+        )]),
+        normal_field: "test_value".to_string(),
+    };
+
+    let complex_bytes = fory.serialize(&complex).unwrap();
+    let decoded_complex: ComplexSkipExample = 
fory.deserialize(&complex_bytes).unwrap();
+
+    assert_eq!(decoded_complex.normal_field, "test_value");
+
+    assert_eq!(decoded_complex.boxed_dyn.name(), "".to_string());
+    assert!(decoded_complex.animals_rc.is_empty());
+    assert!(decoded_complex.animals_arc.is_empty());
+    assert!(decoded_complex.registry.is_empty());
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to