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 4d1679205 fix(rust): output original registered ID in type mismatch
error log (#3067)
4d1679205 is described below
commit 4d167920521da97ad4d6d3b8d2e1b9c578a42989
Author: userzhy <[email protected]>
AuthorDate: Sun Dec 21 00:55:28 2025 +0800
fix(rust): output original registered ID in type mismatch error log (#3067)
## Why?
Current Fory Rust's error messages display the combined type ID (after
left-shifting) which is confusing for users. For example:
```text
FORY_PANIC_ON_ERROR: Type mismatch: type_a = 787, type_b = 6
```
Here `787` is the internal combined type ID `(3 << 8) + 19`, but users
registered using `ID=3`. This makes debugging difficult as users cannot
easily correlate the error with their registration code.
## What does this PR do?
This PR improves the type mismatch error message to show the original
registered ID along with the internal type name:
```text
Type mismatch: local registered_id=3(EXT) vs remote INT64
```
**Changes:**
- Added `format_type_id()` function in `types.rs` to parse combined type
IDs and format them into human-readable strings.
- Modified `TypeMismatch` error variant to use the formatted string
message.
- Added test case to verify the new error message format.
## Related issues
Closes #3054
## 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?
The error message format is changed but it only affects the display of
error messages, not the API or binary protocol.
## Benchmark
Not applicable. This change only affects error message formatting in the
error path, which has no impact on serialization/deserialization
performance.
---
rust/fory-core/src/error.rs | 15 ++++++--
rust/fory-core/src/types.rs | 87 +++++++++++++++++++++++++++++++++++++++++++
rust/tests/tests/test_fory.rs | 42 +++++++++++++++++++++
3 files changed, 141 insertions(+), 3 deletions(-)
diff --git a/rust/fory-core/src/error.rs b/rust/fory-core/src/error.rs
index a27fb4d85..d4c5ec488 100644
--- a/rust/fory-core/src/error.rs
+++ b/rust/fory-core/src/error.rs
@@ -29,6 +29,7 @@
use std::borrow::Cow;
+use crate::types::format_type_id;
use thiserror::Error;
/// Global flag to check if FORY_PANIC_ON_ERROR environment variable is set at
compile time.
@@ -108,8 +109,8 @@ pub enum Error {
/// Type mismatch between local and remote type IDs.
///
/// Do not construct this variant directly; use [`Error::type_mismatch`]
instead.
- #[error("Type mismatch: type_a = {0}, type_b = {1}")]
- TypeMismatch(u32, u32),
+ #[error("{0}")]
+ TypeMismatch(Cow<'static, str>),
/// Buffer boundary violation during read/write operations.
///
@@ -187,6 +188,9 @@ pub enum Error {
impl Error {
/// Creates a new [`Error::TypeMismatch`] with the given type IDs.
///
+ /// The error message will display both the original registered ID and the
internal type name
+ /// for user-registered types, making debugging easier.
+ ///
/// If `FORY_PANIC_ON_ERROR` environment variable is set, this will panic
with the error message.
///
/// # Example
@@ -199,7 +203,12 @@ impl Error {
#[cold]
#[track_caller]
pub fn type_mismatch(type_a: u32, type_b: u32) -> Self {
- let err = Error::TypeMismatch(type_a, type_b);
+ let msg = format!(
+ "Type mismatch: local {} vs remote {}",
+ format_type_id(type_a),
+ format_type_id(type_b)
+ );
+ let err = Error::TypeMismatch(Cow::Owned(msg));
if PANIC_ON_ERROR {
panic!("FORY_PANIC_ON_ERROR: {}", err);
}
diff --git a/rust/fory-core/src/types.rs b/rust/fory-core/src/types.rs
index 6090a493e..dfec7d3ed 100644
--- a/rust/fory-core/src/types.rs
+++ b/rust/fory-core/src/types.rs
@@ -400,3 +400,90 @@ impl TryFrom<u8> for Language {
pub const SIZE_OF_REF_AND_TYPE: usize = mem::size_of::<i8>() +
mem::size_of::<i16>();
pub const MAGIC_NUMBER: u16 = 0x62d4;
+
+/// Formats a combined type ID into a human-readable string.
+///
+/// Combined type IDs have the format: `(registered_id << 8) +
internal_type_id`.
+/// This function extracts both parts and formats them for debugging.
+///
+/// For internal types (type_id < BOUND), returns just the type name.
+/// For user-registered types, returns format like "registered_id=3(STRUCT)".
+///
+/// # Examples
+/// ```
+/// use fory_core::types::format_type_id;
+///
+/// // Internal type (e.g., BOOL = 1)
+/// assert_eq!(format_type_id(1), "BOOL");
+///
+/// // User registered struct with id=3: (3 << 8) + 15 = 783
+/// assert_eq!(format_type_id(783), "registered_id=3(STRUCT)");
+/// ```
+pub fn format_type_id(type_id: u32) -> String {
+ let internal_type_id = type_id & 0xff;
+ let registered_id = type_id >> 8;
+
+ let type_name = match internal_type_id {
+ 0 => "UNKNOWN",
+ 1 => "BOOL",
+ 2 => "INT8",
+ 3 => "INT16",
+ 4 => "INT32",
+ 5 => "VAR_INT32",
+ 6 => "INT64",
+ 7 => "VAR_INT64",
+ 8 => "SLI_INT64",
+ 9 => "FLOAT16",
+ 10 => "FLOAT32",
+ 11 => "FLOAT64",
+ 12 => "STRING",
+ 13 => "ENUM",
+ 14 => "NAMED_ENUM",
+ 15 => "STRUCT",
+ 16 => "COMPATIBLE_STRUCT",
+ 17 => "NAMED_STRUCT",
+ 18 => "NAMED_COMPATIBLE_STRUCT",
+ 19 => "EXT",
+ 20 => "NAMED_EXT",
+ 21 => "LIST",
+ 22 => "SET",
+ 23 => "MAP",
+ 24 => "DURATION",
+ 25 => "TIMESTAMP",
+ 26 => "LOCAL_DATE",
+ 27 => "DECIMAL",
+ 28 => "BINARY",
+ 29 => "ARRAY",
+ 30 => "BOOL_ARRAY",
+ 31 => "INT8_ARRAY",
+ 32 => "INT16_ARRAY",
+ 33 => "INT32_ARRAY",
+ 34 => "INT64_ARRAY",
+ 35 => "FLOAT16_ARRAY",
+ 36 => "FLOAT32_ARRAY",
+ 37 => "FLOAT64_ARRAY",
+ 64 => "U8",
+ 65 => "U16",
+ 66 => "U32",
+ 67 => "U64",
+ 68 => "USIZE",
+ 69 => "U128",
+ 70 => "VAR_U32",
+ 71 => "VAR_U64",
+ 72 => "SLI_U64",
+ 73 => "U16_ARRAY",
+ 74 => "U32_ARRAY",
+ 75 => "U64_ARRAY",
+ 76 => "USIZE_ARRAY",
+ 77 => "U128_ARRAY",
+ _ => "UNKNOWN_TYPE",
+ };
+
+ // If it's a pure internal type (no registered_id), just return the type
name
+ if registered_id == 0 {
+ type_name.to_string()
+ } else {
+ // For user-registered types, show both the registered ID and internal
type
+ format!("registered_id={}({})", registered_id, type_name)
+ }
+}
diff --git a/rust/tests/tests/test_fory.rs b/rust/tests/tests/test_fory.rs
index fc7096c35..6ad5df6ed 100644
--- a/rust/tests/tests/test_fory.rs
+++ b/rust/tests/tests/test_fory.rs
@@ -286,3 +286,45 @@ fn test_unregistered_type_error_message() {
err_str
);
}
+
+#[test]
+fn test_type_mismatch_error_shows_registered_id() {
+ use fory_core::error::Error;
+ use fory_core::types::format_type_id;
+
+ // Test internal type (BOOL = 1), no registered_id
+ let formatted = format_type_id(1);
+ assert_eq!(formatted, "BOOL");
+
+ // Test user registered struct with id=3: (3 << 8) + 15(STRUCT) = 783
+ let formatted = format_type_id(783);
+ assert_eq!(formatted, "registered_id=3(STRUCT)");
+
+ // Test user registered enum with id=1: (1 << 8) + 13(ENUM) = 269
+ let formatted = format_type_id(269);
+ assert_eq!(formatted, "registered_id=1(ENUM)");
+
+ // Test user registered EXT with id=3: (3 << 8) + 19(EXT) = 787
+ let formatted = format_type_id(787);
+ assert_eq!(formatted, "registered_id=3(EXT)");
+
+ // Test error message format
+ let err = Error::type_mismatch(783, 269);
+ let err_str = format!("{}", err);
+ assert!(
+ err_str.contains("registered_id=3(STRUCT)"),
+ "error should contain registered_id=3(STRUCT), got: {}",
+ err_str
+ );
+ assert!(
+ err_str.contains("registered_id=1(ENUM)"),
+ "error should contain registered_id=1(ENUM), got: {}",
+ err_str
+ );
+ // Check the message contains "local" and "remote" for clarity
+ assert!(
+ err_str.contains("local") && err_str.contains("remote"),
+ "error should indicate local vs remote types, got: {}",
+ err_str
+ );
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]