This is an automated email from the ASF dual-hosted git repository.

chaokunyang pushed a commit to tag v0.13.2-rc1
in repository https://gitbox.apache.org/repos/asf/fory.git

commit 8fb2eb6681e73236398e1c8ad748ce27ca899551
Author: Shawn Yang <[email protected]>
AuthorDate: Mon Nov 3 23:38:34 2025 +0800

    refactor(rust): merge fory_debug into fory macro attr (#2883)
    
    ## Why?
    
    <!-- Describe the purpose of this PR. -->
    
    ## What does this PR do?
    
    merge fory_debug into fory macro attr
    
    ## Related issues
    
    <!--
    Is there any related issue? If this PR closes them you say say
    fix/closes:
    
    - #xxxx0
    - #xxxx1
    - Fixes #xxxx2
    -->
    
    ## Does this PR introduce any user-facing change?
    
    <!--
    If any user-facing interface changes, please [open an
    issue](https://github.com/apache/fory/issues/new/choose) describing the
    need to do so and update the document if necessary.
    
    Delete section if not applicable.
    -->
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
    
    <!--
    When the PR has an impact on performance (if you don't know whether the
    PR will have an impact on performance, you can submit the PR first, and
    if it will have impact on performance, the code reviewer will explain
    it), be sure to attach a benchmark data here.
    
    Delete section if not applicable.
    -->
---
 AGENTS.md                                       |  3 +-
 rust/README.md                                  |  2 +-
 rust/fory-derive/src/lib.rs                     | 52 ++++++++++++++++++++++---
 rust/fory/src/lib.rs                            |  7 ++--
 rust/tests/tests/compatible/test_container.rs   |  2 +-
 rust/tests/tests/compatible/test_struct.rs      | 20 +++++-----
 rust/tests/tests/compatible/test_struct_enum.rs | 20 +++++-----
 rust/tests/tests/test_cross_language.rs         |  2 +-
 rust/tests/tests/test_debug.rs                  |  2 +-
 rust/tests/tests/test_max_dyn_depth.rs          |  2 +-
 rust/tests/tests/test_one_struct.rs             |  2 +-
 11 files changed, 78 insertions(+), 36 deletions(-)

diff --git a/AGENTS.md b/AGENTS.md
index d1468a686..e3af36933 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -124,6 +124,7 @@ go generate ./...
 - All cargo commands must be executed within the `rust` directory.
 - All changes to `rust` must pass the clippy check and tests.
 - You must set `RUST_BACKTRACE=1 FORY_PANIC_ON_ERROR=1` when debuging rust 
tests to get backtrace.
+- You must add `-- --nocapture` to cargo test command when debuging tests.
 - You must not set `FORY_PANIC_ON_ERROR=1` when runing all rust tests to check 
whether all tests pass, some tests will check Error content, which will fail if 
error just panic.
 
 ```bash
@@ -146,7 +147,7 @@ cargo test -p tests  --test $test_file $test_method
 cargo test --test mod $dir$::$test_file::$test_method
 
 # debug specific test under subdirectory and get backtrace
-RUST_BACKTRACE=1 FORY_PANIC_ON_ERROR=1 ENABLE_FORY_DEBUG_OUTPUT=1 cargo test 
--test mod $dir$::$test_file::$test_method
+RUST_BACKTRACE=1 FORY_PANIC_ON_ERROR=1 ENABLE_FORY_DEBUG_OUTPUT=1 cargo test 
--test mod $dir$::$test_file::$test_method -- --nocapture
 
 # inspect generated code by fory derive macro
 cargo expand --test mod $mod$::$file$ > expanded.rs
diff --git a/rust/README.md b/rust/README.md
index dbdd29da1..bbfa27c55 100644
--- a/rust/README.md
+++ b/rust/README.md
@@ -1049,7 +1049,7 @@ Note: Static data types (non-dynamic types) are secure by 
nature and not subject
 - **Type registry errors**: An error like `TypeId ... not found in type_info 
registry` means the type was never registered with the current `Fory` instance. 
Confirm that every serializable struct or trait implementation calls 
`fory.register::<T>(type_id)` before serialization and that the same IDs are 
reused on the deserialize side.
 - **Quick error lookup**: Prefer the static constructors on 
`fory_core::error::Error` (`Error::type_mismatch`, `Error::invalid_data`, 
`Error::unknown`, etc.) rather than instantiating variants manually. This keeps 
diagnostics consistent and makes opt-in panics work.
 - **Panic on error for backtraces**: Toggle `FORY_PANIC_ON_ERROR=1` (or 
`true`) alongside `RUST_BACKTRACE=1` when running tests or binaries to panic at 
the exact site an error is constructed. Reset the variable afterwards to avoid 
aborting user-facing code paths.
-- **Struct field tracing**: Add the `#[fory_debug]` attribute alongside 
`#[derive(ForyObject)]` to tell the macro to emit hook invocations for that 
type. Once compiled with debug hooks, call `set_before_write_field_func`, 
`set_after_write_field_func`, `set_before_read_field_func`, or 
`set_after_read_field_func` (from `fory-core/src/serializer/struct_.rs`) to 
plug in custom callbacks, and use `reset_struct_debug_hooks()` when you want 
the defaults back.
+- **Struct field tracing**: Add the `#[fory(debug)]` attribute (or 
`#[fory(debug = true)]`) alongside `#[derive(ForyObject)]` to tell the macro to 
emit hook invocations for that type. Once compiled with debug hooks, call 
`set_before_write_field_func`, `set_after_write_field_func`, 
`set_before_read_field_func`, or `set_after_read_field_func` (from 
`fory-core/src/serializer/struct_.rs`) to plug in custom callbacks, and use 
`reset_struct_debug_hooks()` when you want the defaults back.
 - **Lightweight logging**: Without custom hooks, enable 
`ENABLE_FORY_DEBUG_OUTPUT=1` to print field-level read/write events emitted by 
the default hook functions. This is especially useful when investigating 
alignment or cursor mismatches.
 - **Test-time hygiene**: Some integration tests expect `FORY_PANIC_ON_ERROR` 
to remain unset. Export it only for focused debugging sessions, and prefer 
`cargo test --features tests -p tests --test <case>` when isolating failing 
scenarios.
 
diff --git a/rust/fory-derive/src/lib.rs b/rust/fory-derive/src/lib.rs
index 49901b8d6..8ecb4cdbc 100644
--- a/rust/fory-derive/src/lib.rs
+++ b/rust/fory-derive/src/lib.rs
@@ -106,6 +106,14 @@
 //! - Field accessor methods that return references to the underlying data
 //! - Efficient serialization without object allocation
 //!
+//! ## Attributes
+//!
+//! - **`#[fory(debug)]` / `#[fory(debug = true)]`**: Enables per-field debug 
instrumentation
+//!   for the annotated struct, allowing you to install custom hooks via
+//!   `fory_core::serializer::struct_`.
+//! - **`#[fory(skip)]`**: Marks an individual field (or enum variant) to be 
ignored by the
+//!   generated serializer, retaining compatibility with previous releases.
+//!
 //! ## Field Types
 //!
 //! Both macros support a wide range of field types:
@@ -168,7 +176,7 @@
 
 use fory_row::derive_row;
 use proc_macro::TokenStream;
-use syn::{parse_macro_input, DeriveInput};
+use syn::{parse_macro_input, spanned::Spanned, Attribute, DeriveInput, 
LitBool};
 
 mod fory_row;
 mod object;
@@ -198,16 +206,16 @@ mod util;
 ///     city: String,
 /// }
 /// ```
-#[proc_macro_derive(ForyObject, attributes(fory_debug, fory))]
+#[proc_macro_derive(ForyObject, attributes(fory))]
 pub fn proc_macro_derive_fory_object(input: proc_macro::TokenStream) -> 
TokenStream {
     let input = parse_macro_input!(input as DeriveInput);
 
     // Check if this is being applied to a trait (which is not possible with 
derive macros)
     // Derive macros can only be applied to structs, enums, and unions
-    let debug_enabled = input
-        .attrs
-        .iter()
-        .any(|attr| attr.path().is_ident("fory_debug"));
+    let debug_enabled = match parse_debug_flag(&input.attrs) {
+        Ok(flag) => flag,
+        Err(err) => return err.into_compile_error().into(),
+    };
 
     object::derive_serializer(&input, debug_enabled)
 }
@@ -236,3 +244,35 @@ pub fn proc_macro_derive_fory_row(input: 
proc_macro::TokenStream) -> TokenStream
     let input = parse_macro_input!(input as DeriveInput);
     derive_row(&input)
 }
+
+fn parse_debug_flag(attrs: &[Attribute]) -> syn::Result<bool> {
+    let mut debug_flag: Option<bool> = None;
+
+    for attr in attrs {
+        if attr.path().is_ident("fory") {
+            attr.parse_nested_meta(|meta| {
+                if meta.path.is_ident("debug") {
+                    let value = if meta.input.is_empty() {
+                        true
+                    } else {
+                        let lit: LitBool = meta.value()?.parse()?;
+                        lit.value
+                    };
+                    debug_flag = match debug_flag {
+                        Some(existing) if existing != value => {
+                            return Err(syn::Error::new(
+                                meta.path.span(),
+                                "conflicting `debug` attribute values",
+                            ));
+                        }
+                        Some(_) => debug_flag,
+                        None => Some(value),
+                    };
+                }
+                Ok(())
+            })?;
+        }
+    }
+
+    Ok(debug_flag.unwrap_or(false))
+}
diff --git a/rust/fory/src/lib.rs b/rust/fory/src/lib.rs
index b16fc5fc6..a4e85f31d 100644
--- a/rust/fory/src/lib.rs
+++ b/rust/fory/src/lib.rs
@@ -1179,9 +1179,10 @@
 //! - **Panic on error for backtraces**: Set `FORY_PANIC_ON_ERROR=1` (or 
`true`) together with
 //!   `RUST_BACKTRACE=1` while running tests or binaries to panic exactly 
where an error is
 //!   constructed. Unset the variable afterwards so production paths keep 
returning `Result`.
-//! - **Struct field tracing**: Add the `#[fory_debug]` attribute next to 
`#[derive(ForyObject)]`
-//!   when you need per-field instrumentation. Once compiled with debug hooks, 
call
-//!   `set_before_write_field_func`, `set_after_write_field_func`, 
`set_before_read_field_func`, or
+//! - **Struct field tracing**: Add the `#[fory(debug)]` attribute (or 
`#[fory(debug = true)]`)
+//!   next to `#[derive(ForyObject)]` when you need per-field instrumentation. 
Once compiled with
+//!   debug hooks, call `set_before_write_field_func`, 
`set_after_write_field_func`,
+//!   `set_before_read_field_func`, or
 //!   `set_after_read_field_func` from `fory_core::serializer::struct_` to 
install custom
 //!   callbacks, and use `reset_struct_debug_hooks()` to restore defaults.
 //! - **Lightweight logging**: If custom callbacks are unnecessary, enable
diff --git a/rust/tests/tests/compatible/test_container.rs 
b/rust/tests/tests/compatible/test_container.rs
index 63b6d41bb..2076574ef 100644
--- a/rust/tests/tests/compatible/test_container.rs
+++ b/rust/tests/tests/compatible/test_container.rs
@@ -21,7 +21,7 @@ use fory_core::fory::Fory;
 use fory_derive::ForyObject;
 
 #[derive(ForyObject, PartialEq, Eq, Hash, Debug)]
-#[fory_debug]
+#[fory(debug)]
 struct Item {
     id: i32,
 }
diff --git a/rust/tests/tests/compatible/test_struct.rs 
b/rust/tests/tests/compatible/test_struct.rs
index cf41c2f71..62f3f5b59 100644
--- a/rust/tests/tests/compatible/test_struct.rs
+++ b/rust/tests/tests/compatible/test_struct.rs
@@ -142,7 +142,7 @@ fn nonexistent_struct() {
 #[test]
 fn option() {
     #[derive(ForyObject, Debug, PartialEq)]
-    #[fory_debug]
+    #[fory(debug)]
     struct Animal {
         f1: Option<String>,
         f2: Option<String>,
@@ -286,7 +286,7 @@ fn nullable_container() {
 #[test]
 fn inner_nullable() {
     #[derive(ForyObject, Debug)]
-    #[fory_debug]
+    #[fory(debug)]
     pub struct Item1 {
         f1: Vec<Option<String>>,
         f2: HashSet<Option<i8>>,
@@ -295,7 +295,7 @@ fn inner_nullable() {
     }
 
     #[derive(ForyObject, Debug)]
-    #[fory_debug]
+    #[fory(debug)]
     pub struct Item2 {
         f1: Vec<String>,
         f2: HashSet<i8>,
@@ -325,7 +325,7 @@ fn inner_nullable() {
 #[test]
 fn nullable_struct() {
     #[derive(ForyObject, Debug, PartialEq)]
-    #[fory_debug]
+    #[fory(debug)]
     pub struct Item {
         name: String,
         data: Vec<Option<String>>,
@@ -333,7 +333,7 @@ fn nullable_struct() {
     }
 
     #[derive(ForyObject, Debug)]
-    #[fory_debug]
+    #[fory(debug)]
     pub struct Person1 {
         f1: Item,
         f2: Option<Item>,
@@ -342,7 +342,7 @@ fn nullable_struct() {
     }
 
     #[derive(ForyObject, Debug)]
-    #[fory_debug]
+    #[fory(debug)]
     pub struct Person2 {
         f1: Option<Item>,
         f2: Item,
@@ -397,7 +397,7 @@ fn enum_without_payload() {
         Blue,
     }
     #[derive(ForyObject, Debug, PartialEq)]
-    #[fory_debug]
+    #[fory(debug)]
     struct Person1 {
         f1: Color1,
         f2: Color1,
@@ -410,7 +410,7 @@ fn enum_without_payload() {
         last: i8,
     }
     #[derive(ForyObject, Debug, PartialEq)]
-    #[fory_debug]
+    #[fory(debug)]
     struct Person2 {
         // same
         f1: Color1,
@@ -466,7 +466,7 @@ fn named_enum() {
         White,
     }
     #[derive(ForyObject, Debug, PartialEq)]
-    #[fory_debug]
+    #[fory(debug)]
     struct Item1 {
         f1: Color,
         f2: Color,
@@ -481,7 +481,7 @@ fn named_enum() {
         last: i8,
     }
     #[derive(ForyObject, Debug, PartialEq)]
-    #[fory_debug]
+    #[fory(debug)]
     struct Item2 {
         f1: Color,
         f2: Option<Color>,
diff --git a/rust/tests/tests/compatible/test_struct_enum.rs 
b/rust/tests/tests/compatible/test_struct_enum.rs
index 411556a4d..ececef218 100644
--- a/rust/tests/tests/compatible/test_struct_enum.rs
+++ b/rust/tests/tests/compatible/test_struct_enum.rs
@@ -188,7 +188,7 @@ fn nonexistent_struct() {
 #[test]
 fn option() {
     #[derive(ForyObject, Debug, PartialEq)]
-    #[fory_debug]
+    #[fory(debug)]
     struct Animal {
         f1: Option<String>,
         f2: Option<String>,
@@ -332,7 +332,7 @@ fn nullable_container() {
 #[test]
 fn inner_nullable() {
     #[derive(ForyObject, Debug)]
-    #[fory_debug]
+    #[fory(debug)]
     pub struct Item1 {
         f1: Vec<Option<String>>,
         f2: HashSet<Option<i8>>,
@@ -341,7 +341,7 @@ fn inner_nullable() {
     }
 
     #[derive(ForyObject, Debug)]
-    #[fory_debug]
+    #[fory(debug)]
     pub struct Item2 {
         f1: Vec<String>,
         f2: HashSet<i8>,
@@ -371,7 +371,7 @@ fn inner_nullable() {
 #[test]
 fn nullable_struct() {
     #[derive(ForyObject, Debug, PartialEq)]
-    #[fory_debug]
+    #[fory(debug)]
     pub struct Item {
         name: String,
         data: Vec<Option<String>>,
@@ -379,7 +379,7 @@ fn nullable_struct() {
     }
 
     #[derive(ForyObject, Debug)]
-    #[fory_debug]
+    #[fory(debug)]
     pub struct Person1 {
         f1: Item,
         f2: Option<Item>,
@@ -388,7 +388,7 @@ fn nullable_struct() {
     }
 
     #[derive(ForyObject, Debug)]
-    #[fory_debug]
+    #[fory(debug)]
     pub struct Person2 {
         f1: Option<Item>,
         f2: Item,
@@ -443,7 +443,7 @@ fn enum_without_payload() {
         Blue,
     }
     #[derive(ForyObject, Debug, PartialEq)]
-    #[fory_debug]
+    #[fory(debug)]
     struct Person1 {
         f1: Color1,
         f2: Color1,
@@ -456,7 +456,7 @@ fn enum_without_payload() {
         last: i8,
     }
     #[derive(ForyObject, Debug, PartialEq)]
-    #[fory_debug]
+    #[fory(debug)]
     struct Person2 {
         // same
         f1: Color1,
@@ -512,7 +512,7 @@ fn named_enum() {
         White,
     }
     #[derive(ForyObject, Debug, PartialEq)]
-    #[fory_debug]
+    #[fory(debug)]
     struct Item1 {
         f1: Color,
         f2: Color,
@@ -527,7 +527,7 @@ fn named_enum() {
         last: i8,
     }
     #[derive(ForyObject, Debug, PartialEq)]
-    #[fory_debug]
+    #[fory(debug)]
     struct Item2 {
         f1: Color,
         f2: Option<Color>,
diff --git a/rust/tests/tests/test_cross_language.rs 
b/rust/tests/tests/test_cross_language.rs
index d3957f844..1bc64434e 100644
--- a/rust/tests/tests/test_cross_language.rs
+++ b/rust/tests/tests/test_cross_language.rs
@@ -704,7 +704,7 @@ fn test_consistent_named() {
 }
 
 #[derive(ForyObject, Debug, PartialEq)]
-#[fory_debug]
+#[fory(debug)]
 struct VersionCheckStruct {
     f1: i32,
     f2: Option<String>,
diff --git a/rust/tests/tests/test_debug.rs b/rust/tests/tests/test_debug.rs
index 494640517..7c0e86369 100644
--- a/rust/tests/tests/test_debug.rs
+++ b/rust/tests/tests/test_debug.rs
@@ -26,7 +26,7 @@ use fory_core::serializer::struct_::{
 };
 
 #[derive(fory_derive::ForyObject)]
-#[fory_debug]
+#[fory(debug)]
 struct DebugSample {
     a: i32,
     b: String,
diff --git a/rust/tests/tests/test_max_dyn_depth.rs 
b/rust/tests/tests/test_max_dyn_depth.rs
index d1e777adf..521a11303 100644
--- a/rust/tests/tests/test_max_dyn_depth.rs
+++ b/rust/tests/tests/test_max_dyn_depth.rs
@@ -20,7 +20,7 @@ use fory_derive::ForyObject;
 use std::any::Any;
 
 #[derive(ForyObject, Debug)]
-#[fory_debug]
+#[fory(debug)]
 struct Container {
     value: i32,
     nested: Option<Box<dyn Any>>,
diff --git a/rust/tests/tests/test_one_struct.rs 
b/rust/tests/tests/test_one_struct.rs
index e50e0eb73..05bc51de0 100644
--- a/rust/tests/tests/test_one_struct.rs
+++ b/rust/tests/tests/test_one_struct.rs
@@ -24,7 +24,7 @@ fn test_simple() {
     // a single test for cargo expand and analysis: `cargo expand --test 
test_simple_struct 2>&1 > expanded.rs`
     // &["f7", "last", "f2", "f5", "f3", "f6", "f1"]
     #[derive(ForyObject, Debug)]
-    #[fory_debug]
+    #[fory(debug)]
     struct Animal1 {
         f1: HashMap<i8, Vec<i8>>,
         f2: String,


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

Reply via email to