You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fory.apache.org by ch...@apache.org on 2025/10/05 15:12:41 UTC
(fory) branch main updated: feat(rust): support rust dyn trait object serialization (#2691)
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 1221b51e4 feat(rust): support rust dyn trait object serialization (#2691)
1221b51e4 is described below
commit 1221b51e45c0aabd3cb49a2a10d83036f2cb0715
Author: Shawn Yang <sh...@gmail.com>
AuthorDate: Sun Oct 5 23:12:37 2025 +0800
feat(rust): support rust dyn trait object serialization (#2691)
## Why?
Rust dyn trait object serialization support will be very useful for
object-oritiened programing for complex systems
## What does this PR do?
This PR supports serializing trait objects with polymorphism, enabling
dynamic dispatch and type flexibility.
#### Box-Based Trait Objects
Serialize trait objects using `Box<dyn Trait>`:
```rust
use fory_core::{Fory, register_trait_type};
use fory_derive::Fory;
use fory_core::types::Mode;
use fory_core::serializer::Serializer;
trait Animal : Serializer {
fn speak(&self) -> String;
fn name(&self) -> &str;
}
#[derive(Fory)]
struct Dog { name: String, breed: String }
impl Animal for Dog {
fn speak(&self) -> String { "Woof!".to_string() }
fn name(&self) -> &str { &self.name }
}
#[derive(Fory)]
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(Fory)]
struct Zoo {
star_animal: Box<dyn Animal>,
}
let mut fory = Fory::default().mode(Mode::Compatible);
fory.register::<Dog>(100);
fory.register::<Cat>(101);
fory.register::<Zoo>(102);
let zoo = Zoo {
star_animal: Box::new(Dog {
name: "Buddy".to_string(),
breed: "Labrador".to_string(),
}),
};
let serialized = fory.serialize(&zoo);
let deserialized: Zoo = fory.deserialize(&serialized)?;
assert_eq!(deserialized.star_animal.name(), "Buddy");
assert_eq!(deserialized.star_animal.speak(), "Woof!");
```
#### Rc/Arc-Based Trait Objects
For fields with `Rc<dyn Trait>` or `Arc<dyn Trait>`, use them directly
in struct definitions:
```rust
use std::sync::Arc;
use std::rc::Rc;
use std::collections::HashMap;
#[derive(Fory)]
struct AnimalShelter {
animals_rc: Vec<Rc<dyn Animal>>,
animals_arc: Vec<Arc<dyn Animal>>,
registry: HashMap<String, Arc<dyn Animal>>,
}
let mut fory = Fory::default().mode(Mode::Compatible);
fory.register::<Dog>(100);
fory.register::<Cat>(101);
fory.register::<AnimalShelter>(102);
let shelter = AnimalShelter {
animals_rc: vec![
Rc::new(Dog { name: "Rex".to_string(), breed: "Golden".to_string() }),
Rc::new(Cat { name: "Mittens".to_string(), color: "Gray".to_string() }),
],
animals_arc: vec![
Arc::new(Dog { name: "Buddy".to_string(), breed: "Labrador".to_string() }),
],
registry: HashMap::from([
("pet1".to_string(), Arc::new(Dog {
name: "Max".to_string(),
breed: "Shepherd".to_string()
}) as Arc<dyn Animal>),
]),
};
let serialized = fory.serialize(&shelter);
let deserialized: AnimalShelter = fory.deserialize(&serialized)?;
assert_eq!(deserialized.animals_rc[0].name(), "Rex");
assert_eq!(deserialized.animals_arc[0].speak(), "Woof!");
```
**Wrapper Types for Standalone Usage**
Due to Rust's orphan rule, `Rc<dyn Trait>` and `Arc<dyn Trait>` cannot
implement `Serializer` directly. For standalone serialization (not
inside struct fields), use the auto-generated wrapper types:
```rust
// register_trait_type! generates: AnimalRc and AnimalArc
// Wrap Rc/Arc trait objects
let dog_rc: Rc<dyn Animal> = Rc::new(Dog {
name: "Rex".to_string(),
breed: "Golden".to_string()
});
let wrapper = AnimalRc::from(dog_rc);
// Serialize the wrapper
let serialized = fory.serialize(&wrapper);
let deserialized: AnimalRc = fory.deserialize(&serialized)?;
// Unwrap back to Rc<dyn Animal>
let unwrapped: Rc<dyn Animal> = deserialized.unwrap();
assert_eq!(unwrapped.name(), "Rex");
```
## 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.
-->
---
rust/README.md | 131 +++++
rust/benches/src/models/complex.rs | 10 +-
rust/benches/src/models/medium.rs | 6 +-
rust/benches/src/models/realworld.rs | 8 +-
rust/benches/src/models/simple.rs | 6 +-
rust/fory-core/Cargo.toml | 1 +
rust/fory-core/src/fory.rs | 23 +-
rust/fory-core/src/lib.rs | 59 +++
rust/fory-core/src/resolver/type_resolver.rs | 169 +++++-
rust/fory-core/src/serializer/any.rs | 117 ++---
rust/fory-core/src/serializer/arc.rs | 10 +-
rust/fory-core/src/serializer/bool.rs | 8 +
rust/fory-core/src/serializer/box_.rs | 10 +-
rust/fory-core/src/serializer/collection.rs | 34 +-
rust/fory-core/src/serializer/datetime.rs | 16 +
rust/fory-core/src/serializer/enum_.rs | 7 +-
rust/fory-core/src/serializer/list.rs | 13 +-
rust/fory-core/src/serializer/map.rs | 44 +-
rust/fory-core/src/serializer/mod.rs | 74 ++-
rust/fory-core/src/serializer/number.rs | 8 +
rust/fory-core/src/serializer/option.rs | 13 +-
rust/fory-core/src/serializer/rc.rs | 10 +-
rust/fory-core/src/serializer/set.rs | 10 +-
rust/fory-core/src/serializer/string.rs | 8 +
rust/fory-core/src/serializer/trait_object.rs | 715 ++++++++++++++++++++++++++
rust/fory-core/tests/de.rs | 16 -
rust/fory-derive/src/lib.rs | 13 +-
rust/fory-derive/src/object/misc.rs | 26 +-
rust/fory-derive/src/object/read.rs | 434 ++++++++++++----
rust/fory-derive/src/object/serializer.rs | 92 ++++
rust/fory-derive/src/object/util.rs | 148 +++++-
rust/fory-derive/src/object/write.rs | 191 ++++++-
rust/fory-derive/src/util.rs | 129 ++++-
rust/fory/src/lib.rs | 12 +-
rust/tests/tests/test_box.rs | 4 +-
rust/tests/tests/test_compatible.rs | 48 +-
rust/tests/tests/test_complex_struct.rs | 8 +-
rust/tests/tests/test_cross_language.rs | 6 +-
rust/tests/tests/test_ext.rs | 10 +-
rust/tests/tests/test_rc_arc_trait_object.rs | 403 +++++++++++++++
rust/tests/tests/test_trait_object.rs | 564 ++++++++++++++++++++
41 files changed, 3276 insertions(+), 338 deletions(-)
diff --git a/rust/README.md b/rust/README.md
index ca30cc02e..4ac0c0472 100644
--- a/rust/README.md
+++ b/rust/README.md
@@ -91,6 +91,136 @@ let deserialized: Person = fory.deserialize(&serialized)?;
assert_eq!(person, deserialized);
```
+### Trait Object Serialization
+
+Apache Fory™ supports serializing trait objects with polymorphism, enabling dynamic dispatch and type flexibility.
+
+#### Box-Based Trait Objects
+
+Serialize trait objects using `Box<dyn Trait>`:
+
+```rust
+use fory_core::{Fory, register_trait_type};
+use fory_core::serializer::Serializer;
+use fory_derive::Fory;
+use fory_core::types::Mode;
+
+trait Animal: Serializer {
+ fn speak(&self) -> String;
+ fn name(&self) -> &str;
+}
+
+#[derive(Fory)]
+struct Dog { name: String, breed: String }
+
+impl Animal for Dog {
+ fn speak(&self) -> String { "Woof!".to_string() }
+ fn name(&self) -> &str { &self.name }
+}
+
+#[derive(Fory)]
+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(Fory)]
+struct Zoo {
+ star_animal: Box<dyn Animal>,
+}
+
+let mut fory = Fory::default().mode(Mode::Compatible);
+fory.register::<Dog>(100);
+fory.register::<Cat>(101);
+fory.register::<Zoo>(102);
+
+let zoo = Zoo {
+ star_animal: Box::new(Dog {
+ name: "Buddy".to_string(),
+ breed: "Labrador".to_string(),
+ }),
+};
+
+let serialized = fory.serialize(&zoo);
+let deserialized: Zoo = fory.deserialize(&serialized)?;
+
+assert_eq!(deserialized.star_animal.name(), "Buddy");
+assert_eq!(deserialized.star_animal.speak(), "Woof!");
+```
+
+#### Rc/Arc-Based Trait Objects
+
+For fields with `Rc<dyn Trait>` or `Arc<dyn Trait>`, use them directly in struct definitions:
+
+```rust
+use std::sync::Arc;
+use std::rc::Rc;
+use std::collections::HashMap;
+
+#[derive(Fory)]
+struct AnimalShelter {
+ animals_rc: Vec<Rc<dyn Animal>>,
+ animals_arc: Vec<Arc<dyn Animal>>,
+ registry: HashMap<String, Arc<dyn Animal>>,
+}
+
+let mut fory = Fory::default().mode(Mode::Compatible);
+fory.register::<Dog>(100);
+fory.register::<Cat>(101);
+fory.register::<AnimalShelter>(102);
+
+let shelter = AnimalShelter {
+ animals_rc: vec![
+ Rc::new(Dog { name: "Rex".to_string(), breed: "Golden".to_string() }),
+ Rc::new(Cat { name: "Mittens".to_string(), color: "Gray".to_string() }),
+ ],
+ animals_arc: vec![
+ Arc::new(Dog { name: "Buddy".to_string(), breed: "Labrador".to_string() }),
+ ],
+ registry: HashMap::from([
+ ("pet1".to_string(), Arc::new(Dog {
+ name: "Max".to_string(),
+ breed: "Shepherd".to_string()
+ }) as Arc<dyn Animal>),
+ ]),
+};
+
+let serialized = fory.serialize(&shelter);
+let deserialized: AnimalShelter = fory.deserialize(&serialized)?;
+
+assert_eq!(deserialized.animals_rc[0].name(), "Rex");
+assert_eq!(deserialized.animals_arc[0].speak(), "Woof!");
+```
+
+**Wrapper Types for Standalone Usage**
+
+Due to Rust's orphan rule, `Rc<dyn Trait>` and `Arc<dyn Trait>` cannot implement `Serializer` directly. For standalone serialization (not inside struct fields), use the fory auto-generated wrapper types:
+
+```rust
+// register_trait_type! generates: AnimalRc and AnimalArc
+
+// Wrap Rc/Arc trait objects
+let dog_rc: Rc<dyn Animal> = Rc::new(Dog {
+ name: "Rex".to_string(),
+ breed: "Golden".to_string()
+});
+let wrapper = AnimalRc::from(dog_rc);
+
+// Serialize the wrapper
+let serialized = fory.serialize(&wrapper);
+let deserialized: AnimalRc = fory.deserialize(&serialized)?;
+
+// Unwrap back to Rc<dyn Animal>
+let unwrapped: Rc<dyn Animal> = deserialized.unwrap();
+assert_eq!(unwrapped.name(), "Rex");
+```
+
+For struct fields with `Rc<dyn Trait>` and `Arc<dyn Trait>` type, fory will generate code automatically to convert such fields to wrappers for serialization and deserialization.
+
### Row-Based Serialization
For high-performance, zero-copy scenarios:
@@ -190,6 +320,7 @@ assert_eq!(prefs.values().get(0), "en");
- Structs with `#[derive(Fory)]` or `#[derive(ForyRow)]`
- Enums with `#[derive(Fory)]`
+- Trait objects
## ⚡ Performance
diff --git a/rust/benches/src/models/complex.rs b/rust/benches/src/models/complex.rs
index 10e3278c9..80f9fb3b5 100644
--- a/rust/benches/src/models/complex.rs
+++ b/rust/benches/src/models/complex.rs
@@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap;
// Fory models
-#[derive(Fory, Debug, Clone, PartialEq, Default)]
+#[derive(Fory, Debug, Clone, PartialEq)]
pub struct ForyProduct {
pub id: String,
pub name: String,
@@ -31,7 +31,7 @@ pub struct ForyProduct {
pub attributes: HashMap<String, String>,
}
-#[derive(Fory, Debug, Clone, PartialEq, Default)]
+#[derive(Fory, Debug, Clone, PartialEq)]
pub struct ForyOrderItem {
pub product: ForyProduct,
pub quantity: i32,
@@ -39,7 +39,7 @@ pub struct ForyOrderItem {
pub customizations: HashMap<String, String>,
}
-#[derive(Fory, Debug, Clone, PartialEq, Default)]
+#[derive(Fory, Debug, Clone, PartialEq)]
pub struct ForyCustomer {
pub id: String,
pub name: String,
@@ -49,7 +49,7 @@ pub struct ForyCustomer {
pub member_since: NaiveDateTime,
}
-#[derive(Fory, Debug, Clone, PartialEq, Default)]
+#[derive(Fory, Debug, Clone, PartialEq)]
pub struct ForyOrder {
pub id: String,
pub customer: ForyCustomer,
@@ -60,7 +60,7 @@ pub struct ForyOrder {
pub metadata: HashMap<String, String>,
}
-#[derive(Fory, Debug, Clone, PartialEq, Default)]
+#[derive(Fory, Debug, Clone, PartialEq)]
pub struct ECommerceData {
pub orders: Vec<ForyOrder>,
pub customers: Vec<ForyCustomer>,
diff --git a/rust/benches/src/models/medium.rs b/rust/benches/src/models/medium.rs
index c47444bf3..3c114795e 100644
--- a/rust/benches/src/models/medium.rs
+++ b/rust/benches/src/models/medium.rs
@@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap;
// Fory models
-#[derive(Fory, Debug, Clone, PartialEq, Default)]
+#[derive(Fory, Debug, Clone, PartialEq)]
pub struct ForyAddress {
pub street: String,
pub city: String,
@@ -30,7 +30,7 @@ pub struct ForyAddress {
pub zip_code: String,
}
-#[derive(Fory, Debug, Clone, PartialEq, Default)]
+#[derive(Fory, Debug, Clone, PartialEq)]
pub struct Person {
pub name: String,
pub age: i32,
@@ -40,7 +40,7 @@ pub struct Person {
pub created_at: NaiveDateTime,
}
-#[derive(Fory, Debug, Clone, PartialEq, Default)]
+#[derive(Fory, Debug, Clone, PartialEq)]
pub struct Company {
pub name: String,
pub employees: Vec<Person>,
diff --git a/rust/benches/src/models/realworld.rs b/rust/benches/src/models/realworld.rs
index 73f29f8f5..8baa57570 100644
--- a/rust/benches/src/models/realworld.rs
+++ b/rust/benches/src/models/realworld.rs
@@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap;
// Fory models
-#[derive(Fory, Debug, Clone, PartialEq, Default)]
+#[derive(Fory, Debug, Clone, PartialEq)]
pub struct ForyLogEntry {
pub id: String,
pub level: i32, // 0=DEBUG, 1=INFO, 2=WARN, 3=ERROR, 4=FATAL
@@ -34,7 +34,7 @@ pub struct ForyLogEntry {
pub duration_ms: f64,
}
-#[derive(Fory, Debug, Clone, PartialEq, Default)]
+#[derive(Fory, Debug, Clone, PartialEq)]
pub struct ForyUserProfile {
pub user_id: String,
pub username: String,
@@ -45,7 +45,7 @@ pub struct ForyUserProfile {
pub is_active: bool,
}
-#[derive(Fory, Debug, Clone, PartialEq, Default)]
+#[derive(Fory, Debug, Clone, PartialEq)]
pub struct ForyAPIMetrics {
pub endpoint: String,
pub request_count: i64,
@@ -55,7 +55,7 @@ pub struct ForyAPIMetrics {
pub measured_at: NaiveDateTime,
}
-#[derive(Fory, Debug, Clone, PartialEq, Default)]
+#[derive(Fory, Debug, Clone, PartialEq)]
pub struct SystemData {
pub logs: Vec<ForyLogEntry>,
pub users: Vec<ForyUserProfile>,
diff --git a/rust/benches/src/models/simple.rs b/rust/benches/src/models/simple.rs
index 18bc0a6e6..cb82c7fac 100644
--- a/rust/benches/src/models/simple.rs
+++ b/rust/benches/src/models/simple.rs
@@ -21,7 +21,7 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap;
// Fory models
-#[derive(Fory, Debug, Clone, PartialEq, Default)]
+#[derive(Fory, Debug, Clone, PartialEq)]
pub struct SimpleStruct {
pub id: i32,
pub name: String,
@@ -29,13 +29,13 @@ pub struct SimpleStruct {
pub score: f64,
}
-#[derive(Fory, Debug, Clone, PartialEq, Default)]
+#[derive(Fory, Debug, Clone, PartialEq)]
pub struct SimpleList {
pub numbers: Vec<i32>,
pub names: Vec<String>,
}
-#[derive(Fory, Debug, Clone, PartialEq, Default)]
+#[derive(Fory, Debug, Clone, PartialEq)]
pub struct SimpleMap {
pub string_to_int: HashMap<String, i32>,
pub int_to_string: HashMap<i32, String>,
diff --git a/rust/fory-core/Cargo.toml b/rust/fory-core/Cargo.toml
index 280bad7f3..91c91a419 100644
--- a/rust/fory-core/Cargo.toml
+++ b/rust/fory-core/Cargo.toml
@@ -30,6 +30,7 @@ chrono = "0.4"
thiserror = { default-features = false, version = "1.0" }
anyhow = "1"
num_enum = "0.5.1"
+paste = "1.0"
[[bench]]
diff --git a/rust/fory-core/src/fory.rs b/rust/fory-core/src/fory.rs
index 40454ccb9..688a8f34a 100644
--- a/rust/fory-core/src/fory.rs
+++ b/rust/fory-core/src/fory.rs
@@ -143,13 +143,13 @@ impl Fory {
Ok(false)
}
- pub fn deserialize<T: Serializer>(&self, bf: &[u8]) -> Result<T, Error> {
+ pub fn deserialize<T: Serializer + Default>(&self, bf: &[u8]) -> Result<T, Error> {
let reader = Reader::new(bf);
let mut context = ReadContext::new(self, reader);
self.deserialize_with_context(&mut context)
}
- pub fn deserialize_with_context<T: Serializer>(
+ pub fn deserialize_with_context<T: Serializer + Default>(
&self,
context: &mut ReadContext,
) -> Result<T, Error> {
@@ -201,14 +201,14 @@ impl Fory {
&self.type_resolver
}
- pub fn register<T: 'static + StructSerializer + Serializer>(&mut self, id: u32) {
+ pub fn register<T: 'static + StructSerializer + Serializer + Default>(&mut self, id: u32) {
let actual_type_id = T::fory_actual_type_id(id, false, &self.mode);
let type_info =
TypeInfo::new::<T>(self, actual_type_id, &EMPTY_STRING, &EMPTY_STRING, false);
self.type_resolver.register::<T>(&type_info);
}
- pub fn register_by_namespace<T: 'static + StructSerializer + Serializer>(
+ pub fn register_by_namespace<T: 'static + StructSerializer + Serializer + Default>(
&mut self,
namespace: &str,
type_name: &str,
@@ -218,14 +218,14 @@ impl Fory {
self.type_resolver.register::<T>(&type_info);
}
- pub fn register_by_name<T: 'static + StructSerializer + Serializer>(
+ pub fn register_by_name<T: 'static + StructSerializer + Serializer + Default>(
&mut self,
type_name: &str,
) {
self.register_by_namespace::<T>("", type_name);
}
- pub fn register_serializer<T: Serializer>(&mut self, id: u32) {
+ pub fn register_serializer<T: Serializer + Default>(&mut self, id: u32) {
let actual_type_id = get_ext_actual_type_id(id, false);
let type_info = TypeInfo::new_with_empty_def::<T>(
self,
@@ -237,7 +237,11 @@ impl Fory {
self.type_resolver.register_serializer::<T>(&type_info);
}
- pub fn register_serializer_by_name<T: Serializer>(&mut self, type_name: &str, namespace: &str) {
+ pub fn register_serializer_by_name<T: Serializer + Default>(
+ &mut self,
+ type_name: &str,
+ namespace: &str,
+ ) {
let actual_type_id = get_ext_actual_type_id(0, false);
let type_info =
TypeInfo::new_with_empty_def::<T>(self, actual_type_id, namespace, type_name, true);
@@ -249,6 +253,9 @@ pub fn write<T: Serializer>(this: &T, context: &mut WriteContext, is_field: bool
T::fory_write(this, context, is_field);
}
-pub fn read<T: Serializer>(context: &mut ReadContext, is_field: bool) -> Result<T, Error> {
+pub fn read<T: Serializer + Default>(
+ context: &mut ReadContext,
+ is_field: bool,
+) -> Result<T, Error> {
T::fory_read(context, is_field)
}
diff --git a/rust/fory-core/src/lib.rs b/rust/fory-core/src/lib.rs
index 199022419..9ba480d22 100644
--- a/rust/fory-core/src/lib.rs
+++ b/rust/fory-core/src/lib.rs
@@ -52,6 +52,7 @@
//! - Optional types (`Option<T>`)
//! - Date/time types (chrono integration)
//! - Custom structs and enums
+//! - Trait objects (Box, Rc, Arc)
//!
//! ### Performance Optimizations
//!
@@ -60,6 +61,59 @@
//! - **Variable-length encoding** for compact representation
//! - **Little-endian byte order** for cross-platform compatibility
//!
+//! ### Trait Object Serialization
+//!
+//! Fory supports polymorphic serialization through trait objects:
+//!
+//! #### Box-Based Trait Objects
+//!
+//! Define custom traits and register implementations:
+//!
+//! ```ignore
+//! trait Animal {
+//! fn speak(&self) -> String;
+//! }
+//!
+//! #[derive(Fory)]
+//! struct Dog { name: String }
+//!
+//! #[derive(Fory)]
+//! struct Cat { name: String }
+//!
+//! impl Animal for Dog {
+//! fn speak(&self) -> String { "Woof!".to_string() }
+//! fn as_any(&self) -> &dyn std::any::Any { self }
+//! }
+//!
+//! impl Animal for Cat {
+//! fn speak(&self) -> String { "Meow!".to_string() }
+//! fn as_any(&self) -> &dyn std::any::Any { self }
+//! }
+//!
+//! register_trait_type!(Animal, Dog, Cat);
+//!
+//! // Use in struct fields
+//! #[derive(Fory)]
+//! struct Zoo {
+//! star_animal: Box<dyn Animal>,
+//! }
+//! ```
+//!
+//! #### Rc/Arc-Based Trait Objects
+//!
+//! For reference-counted trait objects, use them directly in struct fields:
+//!
+//! ```ignore
+//! #[derive(Fory)]
+//! struct Shelter {
+//! animals_rc: Vec<Rc<dyn Animal>>,
+//! animals_arc: Vec<Arc<dyn Animal>>,
+//! }
+//! ```
+//!
+//! For standalone serialization, use auto-generated wrapper types (e.g., `AnimalRc`, `AnimalArc`)
+//! created by `register_trait_type!` due to Rust's orphan rule limitations.
+//!
//! ## Usage
//!
//! This crate is typically used through the higher-level `fory` crate,
@@ -90,3 +144,8 @@ pub mod row;
pub mod serializer;
pub mod types;
pub mod util;
+
+// Re-export paste for use in macros
+pub use paste;
+
+// Trait object macros are available at crate root due to #[macro_export]
diff --git a/rust/fory-core/src/resolver/type_resolver.rs b/rust/fory-core/src/resolver/type_resolver.rs
index 4b64b561d..63bf513aa 100644
--- a/rust/fory-core/src/resolver/type_resolver.rs
+++ b/rust/fory-core/src/resolver/type_resolver.rs
@@ -29,6 +29,7 @@ use std::{any::Any, collections::HashMap};
type SerializerFn = fn(&dyn Any, &mut WriteContext, is_field: bool);
type DeserializerFn =
fn(&mut ReadContext, is_field: bool, skip_ref_flag: bool) -> Result<Box<dyn Any>, Error>;
+type ToSerializerFn = fn(Box<dyn Any>) -> Result<Box<dyn crate::serializer::Serializer>, Error>;
pub type ExtWriteFn = dyn Fn(&dyn Any, &mut WriteContext, bool) + Send + Sync;
pub type ExtReadFn = dyn Fn(&mut ReadContext, bool) -> Result<Box<dyn Any>, Error> + Send + Sync;
@@ -36,13 +37,19 @@ pub type ExtReadFn = dyn Fn(&mut ReadContext, bool) -> Result<Box<dyn Any>, Erro
pub struct Harness {
serializer: SerializerFn,
deserializer: DeserializerFn,
+ to_serializer: ToSerializerFn,
}
impl Harness {
- pub fn new(serializer: SerializerFn, deserializer: DeserializerFn) -> Harness {
+ pub fn new(
+ serializer: SerializerFn,
+ deserializer: DeserializerFn,
+ to_serializer: ToSerializerFn,
+ ) -> Harness {
Harness {
serializer,
deserializer,
+ to_serializer,
}
}
@@ -53,6 +60,10 @@ impl Harness {
pub fn get_deserializer(&self) -> DeserializerFn {
self.deserializer
}
+
+ pub fn get_to_serializer(&self) -> ToSerializerFn {
+ self.to_serializer
+ }
}
pub struct ExtHarness {
@@ -162,7 +173,6 @@ impl TypeInfo {
}
}
-#[derive(Default)]
pub struct TypeResolver {
serializer_map: HashMap<u32, Harness>,
name_serializer_map: HashMap<(MetaString, MetaString), Harness>,
@@ -178,7 +188,63 @@ pub struct TypeResolver {
const NO_TYPE_ID: u32 = 1000000000;
+impl Default for TypeResolver {
+ fn default() -> Self {
+ let mut resolver = TypeResolver {
+ serializer_map: HashMap::new(),
+ name_serializer_map: HashMap::new(),
+ ext_serializer_map: HashMap::new(),
+ ext_name_serializer_map: HashMap::new(),
+ type_id_map: HashMap::new(),
+ type_name_map: HashMap::new(),
+ type_info_cache: HashMap::new(),
+ type_id_index: Vec::new(),
+ sorted_field_names_map: RefCell::new(HashMap::new()),
+ };
+ resolver.register_builtin_types();
+ resolver
+ }
+}
+
impl TypeResolver {
+ fn register_builtin_types(&mut self) {
+ use crate::types::TypeId;
+
+ macro_rules! register_basic_type {
+ ($ty:ty, $type_id:expr) => {{
+ let type_info = TypeInfo {
+ type_def: vec![],
+ type_id: $type_id as u32,
+ namespace: NAMESPACE_ENCODER
+ .encode_with_encodings("", NAMESPACE_ENCODINGS)
+ .unwrap(),
+ type_name: TYPE_NAME_ENCODER
+ .encode_with_encodings("", TYPE_NAME_ENCODINGS)
+ .unwrap(),
+ register_by_name: false,
+ };
+ self.register_serializer::<$ty>(&type_info);
+ }};
+ }
+
+ register_basic_type!(bool, TypeId::BOOL);
+ register_basic_type!(i8, TypeId::INT8);
+ register_basic_type!(i16, TypeId::INT16);
+ register_basic_type!(i32, TypeId::INT32);
+ register_basic_type!(i64, TypeId::INT64);
+ register_basic_type!(f32, TypeId::FLOAT32);
+ register_basic_type!(f64, TypeId::FLOAT64);
+ register_basic_type!(String, TypeId::STRING);
+
+ register_basic_type!(Vec<bool>, TypeId::BOOL_ARRAY);
+ register_basic_type!(Vec<i8>, TypeId::INT8_ARRAY);
+ register_basic_type!(Vec<i16>, TypeId::INT16_ARRAY);
+ register_basic_type!(Vec<i32>, TypeId::INT32_ARRAY);
+ register_basic_type!(Vec<i64>, TypeId::INT64_ARRAY);
+ register_basic_type!(Vec<f32>, TypeId::FLOAT32_ARRAY);
+ register_basic_type!(Vec<f64>, TypeId::FLOAT64_ARRAY);
+ }
+
pub fn get_type_info(&self, type_id: std::any::TypeId) -> &TypeInfo {
self.type_info_cache.get(&type_id).unwrap_or_else(|| {
panic!(
@@ -203,7 +269,7 @@ impl TypeResolver {
)
}
- pub fn register<T: StructSerializer + Serializer>(&mut self, type_info: &TypeInfo) {
+ pub fn register<T: StructSerializer + Serializer + Default>(&mut self, type_info: &TypeInfo) {
fn serializer<T2: 'static + Serializer>(
this: &dyn Any,
context: &mut WriteContext,
@@ -226,7 +292,7 @@ impl TypeResolver {
}
}
- fn deserializer<T2: 'static + Serializer>(
+ fn deserializer<T2: 'static + Serializer + Default>(
context: &mut ReadContext,
is_field: bool,
skip_ref_flag: bool,
@@ -242,6 +308,17 @@ impl TypeResolver {
}
}
+ fn to_serializer<T2: 'static + Serializer>(
+ boxed_any: Box<dyn Any>,
+ ) -> Result<Box<dyn crate::serializer::Serializer>, Error> {
+ match boxed_any.downcast::<T2>() {
+ Ok(concrete) => Ok(Box::new(*concrete) as Box<dyn crate::serializer::Serializer>),
+ Err(_) => Err(Error::Other(anyhow::anyhow!(
+ "Failed to downcast to concrete type"
+ ))),
+ }
+ }
+
let rs_type_id = std::any::TypeId::of::<T>();
if self.type_info_cache.contains_key(&rs_type_id) {
panic!("rs_struct:{:?} already registered", rs_type_id);
@@ -266,49 +343,97 @@ impl TypeResolver {
);
}
self.type_name_map.insert(rs_type_id, key.clone());
- self.name_serializer_map
- .insert(key, Harness::new(serializer::<T>, deserializer::<T>));
+ self.name_serializer_map.insert(
+ key,
+ Harness::new(serializer::<T>, deserializer::<T>, to_serializer::<T>),
+ );
} else {
let type_id = type_info.type_id;
if self.serializer_map.contains_key(&type_id) {
panic!("TypeId {:?} already registered_by_id", type_id);
}
self.type_id_map.insert(rs_type_id, type_id);
- self.serializer_map
- .insert(type_id, Harness::new(serializer::<T>, deserializer::<T>));
+ self.serializer_map.insert(
+ type_id,
+ Harness::new(serializer::<T>, deserializer::<T>, to_serializer::<T>),
+ );
}
}
- pub fn register_serializer<T: Serializer>(&mut self, type_info: &TypeInfo) {
+ pub fn register_serializer<T: Serializer + Default>(&mut self, type_info: &TypeInfo) {
+ fn serializer<T2: 'static + Serializer>(
+ this: &dyn Any,
+ context: &mut WriteContext,
+ is_field: bool,
+ ) {
+ let this = this.downcast_ref::<T2>();
+ match this {
+ Some(v) => {
+ v.fory_write(context, is_field);
+ }
+ None => todo!(),
+ }
+ }
+
+ fn deserializer<T2: 'static + Serializer + Default>(
+ context: &mut ReadContext,
+ is_field: bool,
+ skip_ref_flag: bool,
+ ) -> Result<Box<dyn Any>, Error> {
+ if skip_ref_flag {
+ match T2::fory_read_data(context, is_field) {
+ Ok(v) => Ok(Box::new(v)),
+ Err(e) => Err(e),
+ }
+ } else {
+ match T2::fory_read(context, is_field) {
+ Ok(v) => Ok(Box::new(v)),
+ Err(e) => Err(e),
+ }
+ }
+ }
+
+ fn to_serializer<T2: 'static + Serializer>(
+ boxed_any: Box<dyn Any>,
+ ) -> Result<Box<dyn crate::serializer::Serializer>, Error> {
+ match boxed_any.downcast::<T2>() {
+ Ok(concrete) => Ok(Box::new(*concrete) as Box<dyn crate::serializer::Serializer>),
+ Err(_) => Err(Error::Other(anyhow::anyhow!(
+ "Failed to downcast to concrete type"
+ ))),
+ }
+ }
+
let rs_type_id = std::any::TypeId::of::<T>();
if self.type_info_cache.contains_key(&rs_type_id) {
panic!("rs_struct:{:?} already registered", rs_type_id);
}
self.type_info_cache.insert(rs_type_id, type_info.clone());
-
- let write_fn: fn(&T, &mut WriteContext, bool) = T::fory_write;
- let read_fn: fn(&mut ReadContext, bool) -> Result<T, Error> = T::fory_read;
if type_info.register_by_name {
let namespace = &type_info.namespace;
let type_name = &type_info.type_name;
let key = (namespace.clone(), type_name.clone());
- if self.ext_name_serializer_map.contains_key(&key) {
+ if self.name_serializer_map.contains_key(&key) {
panic!(
"Namespace:{:?} Name:{:?} already registered_by_name",
namespace, type_name
);
}
self.type_name_map.insert(rs_type_id, key.clone());
- let harness = ExtHarness::new(write_fn, read_fn);
- self.ext_name_serializer_map.insert(key, harness);
+ self.name_serializer_map.insert(
+ key,
+ Harness::new(serializer::<T>, deserializer::<T>, to_serializer::<T>),
+ );
} else {
let type_id = type_info.type_id;
- if self.ext_serializer_map.contains_key(&type_id) {
+ if self.serializer_map.contains_key(&type_id) {
panic!("TypeId {:?} already registered_by_id", type_id);
}
self.type_id_map.insert(rs_type_id, type_id);
- let harness = ExtHarness::new(write_fn, read_fn);
- self.ext_serializer_map.insert(type_id, harness);
+ self.serializer_map.insert(
+ type_id,
+ Harness::new(serializer::<T>, deserializer::<T>, to_serializer::<T>),
+ );
}
}
@@ -354,4 +479,12 @@ impl TypeResolver {
let mut map = self.sorted_field_names_map.borrow_mut();
map.insert(std::any::TypeId::of::<T>(), field_names.to_owned());
}
+
+ pub fn get_fory_type_id(&self, rust_type_id: std::any::TypeId) -> Option<u32> {
+ if let Some(type_info) = self.type_info_cache.get(&rust_type_id) {
+ Some(type_info.get_type_id())
+ } else {
+ self.type_id_map.get(&rust_type_id).copied()
+ }
+ }
}
diff --git a/rust/fory-core/src/serializer/any.rs b/rust/fory-core/src/serializer/any.rs
index b44afd0a4..690579dee 100644
--- a/rust/fory-core/src/serializer/any.rs
+++ b/rust/fory-core/src/serializer/any.rs
@@ -15,73 +15,52 @@
// specific language governing permissions and limitations
// under the License.
-// use crate::error::Error;
-// use crate::fory::Fory;
-// use crate::resolver::context::{ReadContext, WriteContext};
-// use crate::serializer::Serializer;
-// use crate::types::TypeId;
-// use std::any::Any;
+use crate::error::Error;
+use crate::resolver::context::{ReadContext, WriteContext};
+use crate::types::RefFlag;
+use std::any::Any;
-// impl Serializer for Box<dyn Any> {
-// fn reserved_space() -> usize {
-// 0
-// }
-//
-// fn write(&self, _context: &mut WriteContext) {
-// panic!("unreachable")
-// }
-//
-// fn read(_context: &mut ReadContext) -> Result<Self, Error> {
-// panic!("unreachable")
-// }
-//
-// fn get_type_id(_fory: &Fory) -> u32 {
-// TypeId::ForyAny as u32
-// }
-//
-// fn serialize(&self, context: &mut WriteContext) {
-// context
-// .get_fory()
-// .get_type_resolver()
-// .get_harness_by_type(self.as_ref().type_id())
-// .unwrap()
-// .get_serializer()(self.as_ref(), context);
-// }
-//
-// fn deserialize(_context: &mut ReadContext) -> Result<Self, Error> {
-// todo!()
-// // let reset_cursor = context.reader.reset_cursor_to_here();
-// // // ref flag
-// // let ref_flag = context.reader.i8();
-// //
-// // if ref_flag == (RefFlag::NotNullValue as i8) || ref_flag == (RefFlag::RefValue as i8) {
-// // if context.get_fory().get_mode().eq(&Mode::Compatible) {
-// // let type_id = context.meta_resolver.get(meta_index as usize).get_type_id();
-// // reset_cursor(&mut context.reader);
-// // context
-// // .get_fory()
-// // .get_type_resolver()
-// // .get_harness(type_id)
-// // .unwrap()
-// // .get_deserializer()(context)
-// // } else {
-// // let type_id = context.reader.i16();
-// // reset_cursor(&mut context.reader);
-// // context
-// // .get_fory()
-// // .get_type_resolver()
-// // .get_harness(type_id)
-// // .unwrap()
-// // .get_deserializer()(context)
-// // }
-// // } else if ref_flag == (RefFlag::Null as i8) {
-// // Err(anyhow!("Try to deserialize `any` to null"))?
-// // } else if ref_flag == (RefFlag::Ref as i8) {
-// // reset_cursor(&mut context.reader);
-// // Err(Error::Ref)
-// // } else {
-// // reset_cursor(&mut context.reader);
-// // Err(anyhow!("Unknown ref flag, value:{ref_flag}"))?
-// // }
-// }
-// }
+/// Helper function to serialize a Box<dyn Any>
+pub fn serialize_any_box(any_box: &Box<dyn Any>, context: &mut WriteContext, is_field: bool) {
+ context.writer.write_i8(RefFlag::NotNullValue as i8);
+
+ let concrete_type_id = (**any_box).type_id();
+
+ let fory_type_id = context
+ .get_fory()
+ .get_type_resolver()
+ .get_fory_type_id(concrete_type_id)
+ .expect("Type not registered");
+
+ context.writer.write_varuint32(fory_type_id);
+
+ let harness = context
+ .get_fory()
+ .get_type_resolver()
+ .get_harness(fory_type_id)
+ .expect("Harness not found");
+
+ let serializer_fn = harness.get_serializer();
+ serializer_fn(&**any_box, context, is_field);
+}
+
+/// Helper function to deserialize to Box<dyn Any>
+pub fn deserialize_any_box(context: &mut ReadContext) -> Result<Box<dyn Any>, Error> {
+ let ref_flag = context.reader.read_i8();
+ if ref_flag != RefFlag::NotNullValue as i8 {
+ return Err(Error::Other(anyhow::anyhow!(
+ "Expected NotNullValue for Box<dyn Any>"
+ )));
+ }
+
+ let fory_type_id = context.reader.read_varuint32();
+
+ let harness = context
+ .get_fory()
+ .get_type_resolver()
+ .get_harness(fory_type_id)
+ .ok_or_else(|| Error::Other(anyhow::anyhow!("Type {} not registered", fory_type_id)))?;
+
+ let deserializer_fn = harness.get_deserializer();
+ deserializer_fn(context, true, true)
+}
diff --git a/rust/fory-core/src/serializer/arc.rs b/rust/fory-core/src/serializer/arc.rs
index 68b38d228..812dee35d 100644
--- a/rust/fory-core/src/serializer/arc.rs
+++ b/rust/fory-core/src/serializer/arc.rs
@@ -23,7 +23,7 @@ use crate::types::RefFlag;
use anyhow::anyhow;
use std::sync::Arc;
-impl<T: Serializer + Send + Sync + 'static> Serializer for Arc<T> {
+impl<T: Serializer + Default + Send + Sync + 'static> Serializer for Arc<T> {
fn fory_read_data(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
let ref_flag = context.ref_reader.read_ref_flag(&mut context.reader);
@@ -70,4 +70,12 @@ impl<T: Serializer + Send + Sync + 'static> Serializer for Arc<T> {
fn fory_get_type_id(fory: &Fory) -> u32 {
T::fory_get_type_id(fory)
}
+
+ fn fory_type_id_dyn(&self, fory: &Fory) -> u32 {
+ (**self).fory_type_id_dyn(fory)
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
}
diff --git a/rust/fory-core/src/serializer/bool.rs b/rust/fory-core/src/serializer/bool.rs
index b376fcd3c..3568d3950 100644
--- a/rust/fory-core/src/serializer/bool.rs
+++ b/rust/fory-core/src/serializer/bool.rs
@@ -39,4 +39,12 @@ impl Serializer for bool {
fn fory_get_type_id(_fory: &Fory) -> u32 {
TypeId::BOOL as u32
}
+
+ fn fory_type_id_dyn(&self, _fory: &Fory) -> u32 {
+ TypeId::BOOL as u32
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
}
diff --git a/rust/fory-core/src/serializer/box_.rs b/rust/fory-core/src/serializer/box_.rs
index fc2d5d003..8a63236d6 100644
--- a/rust/fory-core/src/serializer/box_.rs
+++ b/rust/fory-core/src/serializer/box_.rs
@@ -21,7 +21,7 @@ use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
-impl<T: Serializer> Serializer for Box<T> {
+impl<T: Serializer + Default> Serializer for Box<T> {
fn fory_read_data(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
Ok(Box::new(T::fory_read_data(context, is_field)?))
}
@@ -45,4 +45,12 @@ impl<T: Serializer> Serializer for Box<T> {
fn fory_get_type_id(fory: &Fory) -> u32 {
T::fory_get_type_id(fory)
}
+
+ fn fory_type_id_dyn(&self, fory: &Fory) -> u32 {
+ (**self).fory_type_id_dyn(fory)
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
}
diff --git a/rust/fory-core/src/serializer/collection.rs b/rust/fory-core/src/serializer/collection.rs
index 2c700a0c8..b3ddf77ec 100644
--- a/rust/fory-core/src/serializer/collection.rs
+++ b/rust/fory-core/src/serializer/collection.rs
@@ -62,7 +62,7 @@ pub fn write_collection<'a, T: Serializer + 'a, I: IntoIterator<Item = &'a T>>(
}
}
}
- let is_same_type = true;
+ let is_same_type = !T::fory_is_polymorphic();
if has_null {
header |= HAS_NULL;
}
@@ -75,10 +75,16 @@ pub fn write_collection<'a, T: Serializer + 'a, I: IntoIterator<Item = &'a T>>(
context.writer.write_u8(header);
T::fory_write_type_info(context, is_field);
// context.writer.reserve((T::reserved_space() + SIZE_OF_REF_AND_TYPE) * len);
- for item in &items {
- // let skip_ref_flag = crate::serializer::get_skip_ref_flag::<T>(context.get_fory());
- let skip_ref_flag = is_same_type && !has_null;
- crate::serializer::write_ref_info_data(*item, context, is_field, skip_ref_flag, true);
+ if T::fory_is_polymorphic() {
+ for item in &items {
+ item.fory_write(context, is_field);
+ }
+ } else {
+ for item in &items {
+ // let skip_ref_flag = crate::serializer::get_skip_ref_flag::<T>(context.get_fory());
+ let skip_ref_flag = is_same_type && !has_null;
+ crate::serializer::write_ref_info_data(*item, context, is_field, skip_ref_flag, true);
+ }
}
}
@@ -95,7 +101,7 @@ pub fn read_collection_type_info(
pub fn read_collection<C, T>(context: &mut ReadContext) -> Result<C, Error>
where
- T: Serializer,
+ T: Serializer + Default,
C: FromIterator<T>,
{
let len = context.reader.read_varuint32();
@@ -107,9 +113,15 @@ where
T::fory_read_type_info(context, declared);
let has_null = (header & HAS_NULL) != 0;
let is_same_type = (header & IS_SAME_TYPE) != 0;
- let skip_ref_flag = is_same_type && !has_null;
- // let skip_ref_flag = crate::serializer::get_skip_ref_flag::<T>(context.get_fory());
- (0..len)
- .map(|_| crate::serializer::read_ref_info_data(context, declared, skip_ref_flag, true))
- .collect::<Result<C, Error>>()
+ if T::fory_is_polymorphic() {
+ (0..len)
+ .map(|_| T::fory_read(context, declared))
+ .collect::<Result<C, Error>>()
+ } else {
+ let skip_ref_flag = is_same_type && !has_null;
+ // let skip_ref_flag = crate::serializer::get_skip_ref_flag::<T>(context.get_fory());
+ (0..len)
+ .map(|_| crate::serializer::read_ref_info_data(context, declared, skip_ref_flag, true))
+ .collect::<Result<C, Error>>()
+ }
}
diff --git a/rust/fory-core/src/serializer/datetime.rs b/rust/fory-core/src/serializer/datetime.rs
index fc537c1b0..7a5c1bd38 100644
--- a/rust/fory-core/src/serializer/datetime.rs
+++ b/rust/fory-core/src/serializer/datetime.rs
@@ -52,6 +52,14 @@ impl Serializer for NaiveDateTime {
fn fory_get_type_id(_fory: &Fory) -> u32 {
TypeId::TIMESTAMP as u32
}
+
+ fn fory_type_id_dyn(&self, _fory: &Fory) -> u32 {
+ TypeId::TIMESTAMP as u32
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
}
impl Serializer for NaiveDate {
@@ -76,4 +84,12 @@ impl Serializer for NaiveDate {
fn fory_get_type_id(_fory: &Fory) -> u32 {
TypeId::LOCAL_DATE as u32
}
+
+ fn fory_type_id_dyn(&self, _fory: &Fory) -> u32 {
+ TypeId::LOCAL_DATE as u32
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
}
diff --git a/rust/fory-core/src/serializer/enum_.rs b/rust/fory-core/src/serializer/enum_.rs
index 43ee68a05..caa9800f7 100644
--- a/rust/fory-core/src/serializer/enum_.rs
+++ b/rust/fory-core/src/serializer/enum_.rs
@@ -89,7 +89,7 @@ pub fn read_type_info<T: Serializer>(context: &mut ReadContext, is_field: bool)
}
#[inline(always)]
-pub fn read_compatible<T: Serializer>(context: &mut ReadContext) -> Result<T, Error> {
+pub fn read_compatible<T: Serializer + Default>(context: &mut ReadContext) -> Result<T, Error> {
T::fory_read_type_info(context, true);
T::fory_read_data(context, true)
}
@@ -102,7 +102,10 @@ pub fn write<T: Serializer>(this: &T, context: &mut WriteContext, is_field: bool
}
#[inline(always)]
-pub fn read<T: Serializer>(context: &mut ReadContext, is_field: bool) -> Result<T, Error> {
+pub fn read<T: Serializer + Default>(
+ context: &mut ReadContext,
+ is_field: bool,
+) -> Result<T, Error> {
let ref_flag = context.reader.read_i8();
if ref_flag == RefFlag::Null as i8 {
Ok(T::default())
diff --git a/rust/fory-core/src/serializer/list.rs b/rust/fory-core/src/serializer/list.rs
index df9ec54bb..89d699a8c 100644
--- a/rust/fory-core/src/serializer/list.rs
+++ b/rust/fory-core/src/serializer/list.rs
@@ -42,7 +42,7 @@ fn check_primitive<T: 'static>() -> Option<TypeId> {
})
}
-impl<T: Serializer> Serializer for Vec<T> {
+impl<T: Serializer + Default> Serializer for Vec<T> {
fn fory_write_data(&self, context: &mut WriteContext, is_field: bool) {
match check_primitive::<T>() {
Some(_) => {
@@ -95,4 +95,15 @@ impl<T: Serializer> Serializer for Vec<T> {
None => TypeId::LIST as u32,
}
}
+
+ fn fory_type_id_dyn(&self, _fory: &Fory) -> u32 {
+ match check_primitive::<T>() {
+ Some(type_id) => type_id as u32,
+ None => TypeId::LIST as u32,
+ }
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
}
diff --git a/rust/fory-core/src/serializer/map.rs b/rust/fory-core/src/serializer/map.rs
index 919a67114..5a4bc875e 100644
--- a/rust/fory-core/src/serializer/map.rs
+++ b/rust/fory-core/src/serializer/map.rs
@@ -80,7 +80,9 @@ fn write_chunk_size(context: &mut WriteContext, header_offset: usize, size: u8)
context.writer.set_bytes(header_offset + 1, &[size]);
}
-impl<K: Serializer + Eq + std::hash::Hash, V: Serializer> Serializer for HashMap<K, V> {
+impl<K: Serializer + Default + Eq + std::hash::Hash, V: Serializer + Default> Serializer
+ for HashMap<K, V>
+{
fn fory_write_data(&self, context: &mut WriteContext, is_field: bool) {
let length = self.len();
context.writer.write_varuint32(length as u32);
@@ -111,9 +113,9 @@ impl<K: Serializer + Eq + std::hash::Hash, V: Serializer> Serializer for HashMap
chunk_header |= DECL_VALUE_TYPE;
}
// skip_key_ref_flag = crate::serializer::get_skip_ref_flag::<K>(context.get_fory());
- skip_key_ref_flag = true;
+ skip_key_ref_flag = !K::fory_is_polymorphic();
// skip_val_ref_flag = crate::serializer::get_skip_ref_flag::<V>(context.get_fory());
- skip_val_ref_flag = true;
+ skip_val_ref_flag = !V::fory_is_polymorphic();
if !skip_key_ref_flag {
chunk_header |= TRACKING_KEY_REF;
}
@@ -132,8 +134,16 @@ impl<K: Serializer + Eq + std::hash::Hash, V: Serializer> Serializer for HashMap
check_and_write_null(context, is_field, key, value);
continue;
}
- write_ref_info_data(key, context, is_field, skip_key_ref_flag, true);
- write_ref_info_data(value, context, is_field, skip_val_ref_flag, true);
+ if K::fory_is_polymorphic() {
+ key.fory_write(context, is_field);
+ } else {
+ write_ref_info_data(key, context, is_field, skip_key_ref_flag, true);
+ }
+ if V::fory_is_polymorphic() {
+ value.fory_write(context, is_field);
+ } else {
+ write_ref_info_data(value, context, is_field, skip_val_ref_flag, true);
+ }
pair_counter += 1;
if pair_counter == MAX_CHUNK_SIZE {
write_chunk_size(context, header_offset, pair_counter);
@@ -196,10 +206,18 @@ impl<K: Serializer + Eq + std::hash::Hash, V: Serializer> Serializer for HashMap
V::fory_read_type_info(context, value_declared);
assert!(len_counter + chunk_size as u32 <= len);
for _ in (0..chunk_size).enumerate() {
- // let skip_ref_flag = crate::serializer::get_skip_ref_flag::<K>(context.get_fory());
- let key = read_ref_info_data(context, key_declared, true, true)?;
- // let skip_ref_flag = crate::serializer::get_skip_ref_flag::<V>(context.get_fory());
- let value = read_ref_info_data(context, value_declared, true, true)?;
+ let key = if K::fory_is_polymorphic() {
+ K::fory_read(context, key_declared)?
+ } else {
+ // let skip_ref_flag = crate::serializer::get_skip_ref_flag::<K>(context.get_fory());
+ read_ref_info_data(context, key_declared, true, true)?
+ };
+ let value = if V::fory_is_polymorphic() {
+ V::fory_read(context, value_declared)?
+ } else {
+ // let skip_ref_flag = crate::serializer::get_skip_ref_flag::<V>(context.get_fory());
+ read_ref_info_data(context, value_declared, true, true)?
+ };
map.insert(key, value);
}
len_counter += chunk_size as u32;
@@ -214,4 +232,12 @@ impl<K: Serializer + Eq + std::hash::Hash, V: Serializer> Serializer for HashMap
fn fory_get_type_id(_fory: &Fory) -> u32 {
TypeId::MAP as u32
}
+
+ fn fory_type_id_dyn(&self, _fory: &Fory) -> u32 {
+ TypeId::MAP as u32
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
}
diff --git a/rust/fory-core/src/serializer/mod.rs b/rust/fory-core/src/serializer/mod.rs
index 7f9ddbae6..bc9c66599 100644
--- a/rust/fory-core/src/serializer/mod.rs
+++ b/rust/fory-core/src/serializer/mod.rs
@@ -21,7 +21,7 @@ use crate::meta::MetaString;
use crate::resolver::context::{ReadContext, WriteContext};
use crate::types::{Mode, RefFlag, PRIMITIVE_TYPES};
-mod any;
+pub mod any;
mod arc;
mod bool;
mod box_;
@@ -38,6 +38,7 @@ mod set;
pub mod skip;
mod string;
pub mod struct_;
+pub mod trait_object;
pub fn write_ref_info_data<T: Serializer + 'static>(
record: &T,
@@ -100,20 +101,26 @@ pub fn get_skip_ref_flag<T: Serializer>(fory: &Fory) -> bool {
!T::fory_is_option() && PRIMITIVE_TYPES.contains(&elem_type_id)
}
-pub trait Serializer
-where
- Self: Sized + Default + 'static,
-{
+pub trait Serializer: 'static {
/// Entry point of the serialization.
- fn fory_write(&self, context: &mut WriteContext, is_field: bool) {
+ fn fory_write(&self, context: &mut WriteContext, is_field: bool)
+ where
+ Self: Sized,
+ {
write_ref_info_data(self, context, is_field, false, false);
}
- fn fory_read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
+ fn fory_read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error>
+ where
+ Self: Sized + Default,
+ {
read_ref_info_data(context, is_field, false, false)
}
- fn fory_is_option() -> bool {
+ fn fory_is_option() -> bool
+ where
+ Self: Sized,
+ {
false
}
@@ -121,26 +128,47 @@ where
false
}
- fn fory_get_type_id(fory: &Fory) -> u32 {
+ fn fory_is_polymorphic() -> bool
+ where
+ Self: Sized,
+ {
+ false
+ }
+
+ fn fory_get_type_id(fory: &Fory) -> u32
+ where
+ Self: Sized,
+ {
fory.get_type_resolver()
.get_type_info(std::any::TypeId::of::<Self>())
.get_type_id()
}
+ fn fory_type_id_dyn(&self, fory: &Fory) -> u32;
+
/// The possible max memory size of the type.
/// Used to reserve the buffer space to avoid reallocation, which may hurt performance.
- fn fory_reserved_space() -> usize {
+ fn fory_reserved_space() -> usize
+ where
+ Self: Sized,
+ {
0
}
- fn fory_write_type_info(context: &mut WriteContext, is_field: bool) {
+ fn fory_write_type_info(context: &mut WriteContext, is_field: bool)
+ where
+ Self: Sized,
+ {
if !is_field {
let type_id = Self::fory_get_type_id(context.get_fory());
context.writer.write_varuint32(type_id);
}
}
- fn fory_read_type_info(context: &mut ReadContext, is_field: bool) {
+ fn fory_read_type_info(context: &mut ReadContext, is_field: bool)
+ where
+ Self: Sized,
+ {
if !is_field {
let remote_type_id = context.reader.read_varuint32();
let local_type_id = Self::fory_get_type_id(context.get_fory());
@@ -151,7 +179,22 @@ where
/// Write/Read the data into the buffer. Need to be implemented.
fn fory_write_data(&self, context: &mut WriteContext, is_field: bool);
- fn fory_read_data(context: &mut ReadContext, is_field: bool) -> Result<Self, Error>;
+ fn fory_read_data(context: &mut ReadContext, is_field: bool) -> Result<Self, Error>
+ where
+ Self: Sized + Default;
+
+ fn fory_concrete_type_id(&self) -> std::any::TypeId {
+ std::any::TypeId::of::<Self>()
+ }
+
+ fn fory_as_any(&self) -> &dyn std::any::Any
+ where
+ Self: Sized,
+ {
+ self
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any;
}
pub trait StructSerializer: Serializer + 'static {
@@ -172,7 +215,10 @@ pub trait StructSerializer: Serializer + 'static {
struct_::actual_type_id(type_id, register_by_name, mode)
}
- fn fory_read_compatible(_context: &mut ReadContext) -> Result<Self, Error> {
+ fn fory_read_compatible(_context: &mut ReadContext) -> Result<Self, Error>
+ where
+ Self: Sized,
+ {
unimplemented!()
}
diff --git a/rust/fory-core/src/serializer/number.rs b/rust/fory-core/src/serializer/number.rs
index 9988b87a5..352e9c0bb 100644
--- a/rust/fory-core/src/serializer/number.rs
+++ b/rust/fory-core/src/serializer/number.rs
@@ -41,6 +41,14 @@ macro_rules! impl_num_serializer {
fn fory_get_type_id(_fory: &Fory) -> u32 {
$field_type as u32
}
+
+ fn fory_type_id_dyn(&self, _fory: &Fory) -> u32 {
+ $field_type as u32
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
}
};
}
diff --git a/rust/fory-core/src/serializer/option.rs b/rust/fory-core/src/serializer/option.rs
index af1a2e4e4..ad59fa9a9 100644
--- a/rust/fory-core/src/serializer/option.rs
+++ b/rust/fory-core/src/serializer/option.rs
@@ -21,7 +21,7 @@ use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
-impl<T: Serializer> Serializer for Option<T> {
+impl<T: Serializer + Default> Serializer for Option<T> {
fn fory_read_data(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
Ok(Some(T::fory_read_data(context, is_field)?))
}
@@ -50,6 +50,13 @@ impl<T: Serializer> Serializer for Option<T> {
T::fory_get_type_id(fory)
}
+ fn fory_type_id_dyn(&self, fory: &Fory) -> u32 {
+ match self {
+ Some(val) => val.fory_type_id_dyn(fory),
+ None => T::fory_get_type_id(fory),
+ }
+ }
+
fn fory_is_option() -> bool {
true
}
@@ -57,4 +64,8 @@ impl<T: Serializer> Serializer for Option<T> {
fn fory_is_none(&self) -> bool {
self.is_none()
}
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
}
diff --git a/rust/fory-core/src/serializer/rc.rs b/rust/fory-core/src/serializer/rc.rs
index de902bd25..8fd1b1c26 100644
--- a/rust/fory-core/src/serializer/rc.rs
+++ b/rust/fory-core/src/serializer/rc.rs
@@ -23,7 +23,7 @@ use crate::types::RefFlag;
use anyhow::anyhow;
use std::rc::Rc;
-impl<T: Serializer + 'static> Serializer for Rc<T> {
+impl<T: Serializer + Default + 'static> Serializer for Rc<T> {
fn fory_read_data(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
let ref_flag = context.ref_reader.read_ref_flag(&mut context.reader);
@@ -70,4 +70,12 @@ impl<T: Serializer + 'static> Serializer for Rc<T> {
fn fory_get_type_id(fory: &Fory) -> u32 {
T::fory_get_type_id(fory)
}
+
+ fn fory_type_id_dyn(&self, fory: &Fory) -> u32 {
+ (**self).fory_type_id_dyn(fory)
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
}
diff --git a/rust/fory-core/src/serializer/set.rs b/rust/fory-core/src/serializer/set.rs
index 824972416..79db7af4c 100644
--- a/rust/fory-core/src/serializer/set.rs
+++ b/rust/fory-core/src/serializer/set.rs
@@ -27,7 +27,7 @@ use crate::types::TypeId;
use std::collections::HashSet;
use std::mem;
-impl<T: Serializer + Eq + std::hash::Hash> Serializer for HashSet<T> {
+impl<T: Serializer + Default + Eq + std::hash::Hash> Serializer for HashSet<T> {
fn fory_write_data(&self, context: &mut WriteContext, is_field: bool) {
write_collection(self, context, is_field);
}
@@ -51,4 +51,12 @@ impl<T: Serializer + Eq + std::hash::Hash> Serializer for HashSet<T> {
fn fory_get_type_id(_fory: &Fory) -> u32 {
TypeId::SET as u32
}
+
+ fn fory_type_id_dyn(&self, _fory: &Fory) -> u32 {
+ TypeId::SET as u32
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
}
diff --git a/rust/fory-core/src/serializer/string.rs b/rust/fory-core/src/serializer/string.rs
index ca1b88a5f..f8c0d390a 100644
--- a/rust/fory-core/src/serializer/string.rs
+++ b/rust/fory-core/src/serializer/string.rs
@@ -87,4 +87,12 @@ impl Serializer for String {
fn fory_get_type_id(_fory: &Fory) -> u32 {
TypeId::STRING as u32
}
+
+ fn fory_type_id_dyn(&self, _fory: &Fory) -> u32 {
+ TypeId::STRING as u32
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
}
diff --git a/rust/fory-core/src/serializer/trait_object.rs b/rust/fory-core/src/serializer/trait_object.rs
new file mode 100644
index 000000000..cf850536b
--- /dev/null
+++ b/rust/fory-core/src/serializer/trait_object.rs
@@ -0,0 +1,715 @@
+// 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 crate::error::Error;
+use crate::fory::Fory;
+use crate::resolver::context::{ReadContext, WriteContext};
+use crate::serializer::Serializer;
+
+/// Helper functions for trait object serialization to reduce code duplication
+///
+/// Writes common trait object headers (ref flag, type ID, compatibility metadata)
+pub fn write_trait_object_headers(
+ context: &mut WriteContext,
+ fory_type_id: u32,
+ concrete_type_id: std::any::TypeId,
+) {
+ use crate::types::{Mode, RefFlag, TypeId};
+
+ context.writer.write_i8(RefFlag::NotNullValue as i8);
+ context.writer.write_varuint32(fory_type_id);
+
+ if context.get_fory().get_mode() == &Mode::Compatible
+ && (fory_type_id & 0xff == TypeId::NAMED_COMPATIBLE_STRUCT as u32
+ || fory_type_id & 0xff == TypeId::COMPATIBLE_STRUCT as u32)
+ {
+ let meta_index = context.push_meta(concrete_type_id) as u32;
+ context.writer.write_varuint32(meta_index);
+ }
+}
+
+/// Reads common trait object headers and returns the type ID
+pub fn read_trait_object_headers(context: &mut ReadContext) -> Result<u32, Error> {
+ use crate::types::{Mode, RefFlag, TypeId};
+
+ let ref_flag = context.reader.read_i8();
+ if ref_flag != RefFlag::NotNullValue as i8 {
+ return Err(Error::Other(crate::error::AnyhowError::msg(format!(
+ "Expected NotNullValue ref flag, got {}",
+ ref_flag
+ ))));
+ }
+
+ let fory_type_id = context.reader.read_varuint32();
+
+ if context.get_fory().get_mode() == &Mode::Compatible
+ && (fory_type_id & 0xff == TypeId::NAMED_COMPATIBLE_STRUCT as u32
+ || fory_type_id & 0xff == TypeId::COMPATIBLE_STRUCT as u32)
+ {
+ let _meta_index = context.reader.read_varuint32();
+ }
+
+ Ok(fory_type_id)
+}
+
+/// Helper macro for common type resolution and downcasting pattern
+#[macro_export]
+macro_rules! downcast_and_serialize {
+ ($any_ref:expr, $context:expr, $is_field:expr, $trait_name:ident, $($impl_type:ty),+) => {{
+ $(
+ if $any_ref.type_id() == std::any::TypeId::of::<$impl_type>() {
+ if let Some(concrete) = $any_ref.downcast_ref::<$impl_type>() {
+ concrete.fory_write_data($context, $is_field);
+ return;
+ }
+ }
+ )*
+ panic!("Failed to downcast to any registered type for trait {}", stringify!($trait_name));
+ }};
+}
+
+/// Helper macro for common type resolution and deserialization pattern
+#[macro_export]
+macro_rules! resolve_and_deserialize {
+ ($fory_type_id:expr, $context:expr, $is_field:expr, $constructor:expr, $trait_name:ident, $($impl_type:ty),+) => {{
+ $(
+ if let Some(registered_type_id) = $context.get_fory().get_type_resolver().get_fory_type_id(std::any::TypeId::of::<$impl_type>()) {
+ if $fory_type_id == registered_type_id {
+ let concrete_obj = <$impl_type as $crate::serializer::Serializer>::fory_read_data($context, $is_field)?;
+ return Ok($constructor(concrete_obj));
+ }
+ }
+ )*
+ Err($crate::error::Error::Other($crate::error::AnyhowError::msg(
+ format!("Type ID {} not registered for trait {}", $fory_type_id, stringify!($trait_name))
+ )))
+ }};
+}
+
+/// Macro to register trait object conversions for custom traits.
+///
+/// This macro automatically generates serializers for `Box<dyn Trait>` trait objects.
+/// Due to Rust's orphan rules, only Box<dyn Trait> is supported for user-defined traits.
+///
+/// The macro generates:
+/// - `Serializer` implementation for `Box<dyn Trait>`
+/// - `Default` implementation for `Box<dyn Trait>` (uses first registered type)
+/// - `from_any_internal()` helper for deserializing trait objects
+///
+/// **Note**: Your trait must extend the `Serializer` trait.
+/// The `as_any()` method is automatically provided by the `Serializer` trait.
+///
+/// # Example
+///
+/// ```rust,ignore
+/// use fory_core::register_trait_type;
+/// use fory_core::serializer::Serializer;
+/// use fory_derive::Fory;
+///
+/// trait Animal: Serializer {
+/// fn speak(&self) -> String;
+/// fn name(&self) -> &str;
+/// }
+///
+/// #[derive(Fory)]
+/// struct Dog { name: String }
+///
+/// #[derive(Fory)]
+/// struct Cat { name: String }
+///
+/// impl Animal for Dog {
+/// fn speak(&self) -> String { "Woof!".to_string() }
+/// fn name(&self) -> &str { &self.name }
+/// }
+///
+/// impl Animal for Cat {
+/// fn speak(&self) -> String { "Meow!".to_string() }
+/// fn name(&self) -> &str { &self.name }
+/// }
+///
+/// // Register the trait and its implementations
+/// register_trait_type!(Animal, Dog, Cat);
+/// ```
+#[macro_export]
+macro_rules! register_trait_type {
+ ($trait_name:ident, $($impl_type:ty),+ $(,)?) => {
+ // 1. Generate Box<dyn Trait> serializer (existing functionality)
+ // Default implementation using first registered type
+ impl std::default::Default for Box<dyn $trait_name> {
+ fn default() -> Self {
+ Box::new(<register_trait_type!(@first_type $($impl_type),+) as std::default::Default>::default())
+ }
+ }
+
+ // 2. Auto-generate Rc wrapper type and conversions
+ $crate::generate_smart_pointer_wrapper!(Rc, $trait_name, $($impl_type),+);
+
+ // 3. Auto-generate Arc wrapper type and conversions
+ $crate::generate_smart_pointer_wrapper!(Arc, $trait_name, $($impl_type),+);
+
+ // 4. Skip registration helper function for wrapper types - wrappers are not registered
+
+ // 5. Serializer implementation for Box<dyn Trait> (existing functionality)
+ impl $crate::serializer::Serializer for Box<dyn $trait_name> {
+ fn fory_write(&self, context: &mut $crate::resolver::context::WriteContext, is_field: bool) {
+ let any_ref = <dyn $trait_name as $crate::serializer::Serializer>::as_any(&**self);
+ let concrete_type_id = any_ref.type_id();
+
+ if let Some(fory_type_id) = context.get_fory().get_type_resolver().get_fory_type_id(concrete_type_id) {
+ $crate::serializer::trait_object::write_trait_object_headers(context, fory_type_id, concrete_type_id);
+ $crate::downcast_and_serialize!(any_ref, context, is_field, $trait_name, $($impl_type),+);
+ } else {
+ panic!("Type {:?} not registered for Box<dyn {}> serialization", concrete_type_id, stringify!($trait_name));
+ }
+ }
+
+ fn fory_write_data(&self, context: &mut $crate::resolver::context::WriteContext, is_field: bool) {
+ // Delegate to fory_write since this handles the polymorphic dispatch
+ self.fory_write(context, is_field);
+ }
+
+ fn fory_type_id_dyn(&self, fory: &$crate::fory::Fory) -> u32 {
+ let any_ref = <dyn $trait_name as $crate::serializer::Serializer>::as_any(&**self);
+ let concrete_type_id = any_ref.type_id();
+ fory.get_type_resolver()
+ .get_fory_type_id(concrete_type_id)
+ .expect("Type not registered for trait object")
+ }
+
+ fn fory_is_polymorphic() -> bool {
+ true
+ }
+
+ fn fory_write_type_info(_context: &mut $crate::resolver::context::WriteContext, _is_field: bool) {
+ // Box<dyn Trait> is polymorphic - type info is written per element
+ }
+
+ fn fory_read_type_info(_context: &mut $crate::resolver::context::ReadContext, _is_field: bool) {
+ // Box<dyn Trait> is polymorphic - type info is read per element
+ }
+
+ fn fory_read(context: &mut $crate::resolver::context::ReadContext, is_field: bool) -> Result<Self, $crate::error::Error>
+ where
+ Self: Sized + Default,
+ {
+ let fory_type_id = $crate::serializer::trait_object::read_trait_object_headers(context)?;
+ $crate::resolve_and_deserialize!(
+ fory_type_id, context, is_field,
+ |obj| Box::new(obj) as Box<dyn $trait_name>,
+ $trait_name, $($impl_type),+
+ )
+ }
+
+ fn fory_read_data(_context: &mut $crate::resolver::context::ReadContext, _is_field: bool) -> Result<Self, $crate::error::Error>
+ where
+ Self: Sized + Default,
+ {
+ // This should not be called for polymorphic types like Box<dyn Trait>
+ // The fory_read method handles the polymorphic dispatch
+ panic!("fory_read_data should not be called directly on polymorphic Box<dyn {}> trait object", stringify!($trait_name));
+ }
+
+ fn fory_get_type_id(_fory: &$crate::fory::Fory) -> u32 {
+ $crate::types::TypeId::STRUCT as u32
+ }
+
+ fn fory_reserved_space() -> usize {
+ $crate::types::SIZE_OF_REF_AND_TYPE
+ }
+
+ fn fory_concrete_type_id(&self) -> std::any::TypeId {
+ <dyn $trait_name as $crate::serializer::Serializer>::as_any(&**self).type_id()
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ <dyn $trait_name as $crate::serializer::Serializer>::as_any(&**self)
+ }
+ }
+
+ // Create helper functions for this trait
+ #[allow(non_snake_case)]
+ mod __fory_trait_helpers {
+ use super::*;
+
+ #[allow(dead_code)]
+ pub fn from_any_internal(
+ any_box: Box<dyn std::any::Any>,
+ _fory_type_id: u32,
+ ) -> Result<Box<dyn $trait_name>, $crate::error::Error> {
+ $(
+ if any_box.is::<$impl_type>() {
+ let concrete = any_box.downcast::<$impl_type>()
+ .map_err(|_| $crate::error::Error::Other(
+ $crate::error::AnyhowError::msg(format!("Failed to downcast to {}", stringify!($impl_type)))
+ ))?;
+ return Ok(Box::new(*concrete) as Box<dyn $trait_name>);
+ }
+ )+
+
+ Err($crate::error::Error::Other($crate::error::AnyhowError::msg(
+ format!("No matching type found for trait {}", stringify!($trait_name))
+ )))
+ }
+ }
+ };
+
+ // Helper to get first type for Default impl
+ (@first_type $first_type:ty $(, $rest:ty)*) => {
+ $first_type
+ };
+}
+
+/// Unified macro to generate smart pointer wrapper types for traits
+/// Supports both Rc and Arc pointer types
+#[macro_export]
+macro_rules! generate_smart_pointer_wrapper {
+ (Rc, $trait_name:ident, $($impl_type:ty),+ $(,)?) => {
+ $crate::paste::paste! {
+ #[derive(Clone)]
+ pub struct [<$trait_name Rc>](std::rc::Rc<dyn $trait_name>);
+
+ impl [<$trait_name Rc>] {
+ pub fn new(inner: std::rc::Rc<dyn $trait_name>) -> Self {
+ Self(inner)
+ }
+
+ pub fn into_inner(self) -> std::rc::Rc<dyn $trait_name> {
+ self.0
+ }
+
+ pub fn unwrap(self) -> std::rc::Rc<dyn $trait_name> {
+ self.0
+ }
+
+ pub fn as_ref(&self) -> &dyn $trait_name {
+ &*self.0
+ }
+ }
+
+ impl From<std::rc::Rc<dyn $trait_name>> for [<$trait_name Rc>] {
+ fn from(ptr: std::rc::Rc<dyn $trait_name>) -> Self {
+ Self::new(ptr)
+ }
+ }
+
+ impl From<[<$trait_name Rc>]> for std::rc::Rc<dyn $trait_name> {
+ fn from(wrapper: [<$trait_name Rc>]) -> Self {
+ wrapper.into_inner()
+ }
+ }
+
+ impl std::default::Default for [<$trait_name Rc>] {
+ fn default() -> Self {
+ Self(std::rc::Rc::new(<register_trait_type!(@first_type $($impl_type),+) as std::default::Default>::default()))
+ }
+ }
+
+ impl std::fmt::Debug for [<$trait_name Rc>] {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let any_obj = <dyn $trait_name as $crate::serializer::Serializer>::as_any(&*self.0);
+ $(
+ if let Some(concrete) = any_obj.downcast_ref::<$impl_type>() {
+ return write!(f, "{}Rc({:?})", stringify!($trait_name), concrete);
+ }
+ )*
+ write!(f, "{}Rc({:p})", stringify!($trait_name), &*self.0)
+ }
+ }
+
+ $crate::impl_smart_pointer_serializer!([<$trait_name Rc>], std::rc::Rc::new, std::rc::Rc<dyn $trait_name>, $trait_name, $($impl_type),+);
+ }
+ };
+
+ (Arc, $trait_name:ident, $($impl_type:ty),+ $(,)?) => {
+ $crate::paste::paste! {
+ #[derive(Clone)]
+ pub struct [<$trait_name Arc>](std::sync::Arc<dyn $trait_name>);
+
+ impl [<$trait_name Arc>] {
+ pub fn new(inner: std::sync::Arc<dyn $trait_name>) -> Self {
+ Self(inner)
+ }
+
+ pub fn into_inner(self) -> std::sync::Arc<dyn $trait_name> {
+ self.0
+ }
+
+ pub fn unwrap(self) -> std::sync::Arc<dyn $trait_name> {
+ self.0
+ }
+
+ pub fn as_ref(&self) -> &dyn $trait_name {
+ &*self.0
+ }
+ }
+
+ impl From<std::sync::Arc<dyn $trait_name>> for [<$trait_name Arc>] {
+ fn from(ptr: std::sync::Arc<dyn $trait_name>) -> Self {
+ Self::new(ptr)
+ }
+ }
+
+ impl From<[<$trait_name Arc>]> for std::sync::Arc<dyn $trait_name> {
+ fn from(wrapper: [<$trait_name Arc>]) -> Self {
+ wrapper.into_inner()
+ }
+ }
+
+ impl std::default::Default for [<$trait_name Arc>] {
+ fn default() -> Self {
+ Self(std::sync::Arc::new(<register_trait_type!(@first_type $($impl_type),+) as std::default::Default>::default()))
+ }
+ }
+
+ impl std::fmt::Debug for [<$trait_name Arc>] {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let any_obj = <dyn $trait_name as $crate::serializer::Serializer>::as_any(&*self.0);
+ $(
+ if let Some(concrete) = any_obj.downcast_ref::<$impl_type>() {
+ return write!(f, "{}Arc({:?})", stringify!($trait_name), concrete);
+ }
+ )*
+ write!(f, "{}Arc({:p})", stringify!($trait_name), &*self.0)
+ }
+ }
+
+ $crate::impl_smart_pointer_serializer!([<$trait_name Arc>], std::sync::Arc::new, std::sync::Arc<dyn $trait_name>, $trait_name, $($impl_type),+);
+ }
+ };
+}
+
+/// Shared serializer implementation for smart pointer wrappers
+#[macro_export]
+macro_rules! impl_smart_pointer_serializer {
+ ($wrapper_name:ident, $constructor_expr:expr, $pointer_type:ty, $trait_name:ident, $($impl_type:ty),+) => {
+ impl $crate::serializer::Serializer for $wrapper_name {
+ fn fory_write(&self, context: &mut $crate::resolver::context::WriteContext, is_field: bool) {
+ let any_ref = <dyn $trait_name as $crate::serializer::Serializer>::as_any(&*self.0);
+ let concrete_type_id = any_ref.type_id();
+
+ if let Some(fory_type_id) = context.get_fory().get_type_resolver().get_fory_type_id(concrete_type_id) {
+ $crate::serializer::trait_object::write_trait_object_headers(context, fory_type_id, concrete_type_id);
+ $crate::downcast_and_serialize!(any_ref, context, is_field, $trait_name, $($impl_type),+);
+ } else {
+ panic!("Type {:?} not registered for {} serialization", concrete_type_id, stringify!($wrapper_name));
+ }
+ }
+
+ fn fory_write_data(&self, context: &mut $crate::resolver::context::WriteContext, is_field: bool) {
+ let any_obj = <dyn $trait_name as $crate::serializer::Serializer>::as_any(&*self.0);
+ $crate::downcast_and_serialize!(any_obj, context, is_field, $trait_name, $($impl_type),+);
+ }
+
+ fn fory_read(context: &mut $crate::resolver::context::ReadContext, is_field: bool) -> Result<Self, $crate::error::Error>
+ where
+ Self: Sized + Default,
+ {
+ let fory_type_id = $crate::serializer::trait_object::read_trait_object_headers(context)?;
+
+ // Use type resolver to deserialize any registered type
+ let type_resolver = context.get_fory().get_type_resolver();
+ if let Some(harness) = type_resolver.get_harness(fory_type_id) {
+ let deserializer_fn = harness.get_deserializer();
+ if let Ok(any_obj) = deserializer_fn(context, is_field, true) {
+ $(
+ if any_obj.is::<$impl_type>() {
+ match any_obj.downcast::<$impl_type>() {
+ Ok(boxed) => {
+ let pointer = $constructor_expr(*boxed) as $pointer_type;
+ return Ok(Self::from(pointer));
+ }
+ Err(recovered) => {
+ return Err($crate::error::Error::Other($crate::error::AnyhowError::msg(
+ format!("Failed to downcast type for trait {}", stringify!($trait_name))
+ )));
+ }
+ }
+ }
+ )*
+ return Err($crate::error::Error::Other($crate::error::AnyhowError::msg(
+ format!("Type ID {} is registered but doesn't implement trait {}", fory_type_id, stringify!($trait_name))
+ )));
+ }
+ }
+
+ Err($crate::error::Error::Other($crate::error::AnyhowError::msg(
+ format!("Type ID {} not registered in Fory", fory_type_id)
+ )))
+ }
+
+ fn fory_read_data(context: &mut $crate::resolver::context::ReadContext, is_field: bool) -> Result<Self, $crate::error::Error>
+ where
+ Self: Sized + Default,
+ {
+ let concrete_fory_type_id = context.reader.read_varuint32();
+ $crate::resolve_and_deserialize!(
+ concrete_fory_type_id, context, is_field,
+ |obj| {
+ let pointer = $constructor_expr(obj) as $pointer_type;
+ Self::from(pointer)
+ },
+ $trait_name, $($impl_type),+
+ )
+ }
+
+ fn fory_get_type_id(_fory: &$crate::fory::Fory) -> u32 {
+ $crate::types::TypeId::STRUCT as u32
+ }
+
+ fn fory_write_type_info(_context: &mut $crate::resolver::context::WriteContext, _is_field: bool) {
+ // Wrapper types are polymorphic - type info is written per element
+ }
+
+ fn fory_read_type_info(_context: &mut $crate::resolver::context::ReadContext, _is_field: bool) {
+ // Wrapper types are polymorphic - type info is read per element
+ }
+
+ fn fory_is_polymorphic() -> bool {
+ true
+ }
+
+ fn fory_type_id_dyn(&self, fory: &$crate::fory::Fory) -> u32 {
+ let any_obj = <dyn $trait_name as $crate::serializer::Serializer>::as_any(&*self.0);
+ let concrete_type_id = any_obj.type_id();
+ fory.get_type_resolver()
+ .get_fory_type_id(concrete_type_id)
+ .expect("Type not registered for trait object")
+ }
+
+ fn fory_concrete_type_id(&self) -> std::any::TypeId {
+ <dyn $trait_name as $crate::serializer::Serializer>::as_any(&*self.0).type_id()
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ <dyn $trait_name as $crate::serializer::Serializer>::as_any(&*self.0)
+ }
+ }
+
+ };
+}
+
+// Keep the existing Box<dyn Serializer> implementation as is
+impl Default for Box<dyn Serializer> {
+ fn default() -> Self {
+ panic!("Box<dyn Serializer> cannot be default-constructed")
+ }
+}
+
+impl Serializer for Box<dyn Serializer> {
+ fn fory_write(&self, context: &mut WriteContext, is_field: bool)
+ where
+ Self: Sized,
+ {
+ let fory_type_id = (**self).fory_type_id_dyn(context.get_fory());
+ let concrete_type_id = (**self).fory_concrete_type_id();
+
+ write_trait_object_headers(context, fory_type_id, concrete_type_id);
+ (**self).fory_write_data(context, is_field);
+ }
+
+ fn fory_write_data(&self, _context: &mut WriteContext, _is_field: bool) {
+ panic!("fory_write_data should not be called directly on Box<dyn Serializer>");
+ }
+
+ fn fory_type_id_dyn(&self, fory: &Fory) -> u32 {
+ (**self).fory_type_id_dyn(fory)
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ (**self).as_any()
+ }
+
+ fn fory_is_polymorphic() -> bool {
+ true
+ }
+
+ fn fory_write_type_info(_context: &mut WriteContext, _is_field: bool) {
+ // Box<dyn Serializer> is polymorphic - type info is written per element
+ }
+
+ fn fory_read_type_info(_context: &mut ReadContext, _is_field: bool) {
+ // Box<dyn Serializer> is polymorphic - type info is read per element
+ }
+
+ fn fory_read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error>
+ where
+ Self: Sized + Default,
+ {
+ let fory_type_id = read_trait_object_headers(context)?;
+
+ let type_resolver = context.get_fory().get_type_resolver();
+
+ if let Some(harness) = type_resolver.get_harness(fory_type_id) {
+ let deserializer_fn = harness.get_deserializer();
+ let to_serializer_fn = harness.get_to_serializer();
+ let boxed_any = deserializer_fn(context, is_field, true)?;
+ let trait_object = to_serializer_fn(boxed_any)?;
+ Ok(trait_object)
+ } else {
+ use crate::types::TypeId;
+ match fory_type_id {
+ id if id == TypeId::LIST as u32 => {
+ Err(Error::Other(anyhow::anyhow!(
+ "Cannot deserialize LIST type ID {} as Box<dyn Serializer> without knowing concrete type. \
+ Use concrete type instead (e.g., Vec<String>)",
+ fory_type_id
+ )))
+ }
+ id if id == TypeId::MAP as u32 => {
+ Err(Error::Other(anyhow::anyhow!(
+ "Cannot deserialize MAP type ID {} as Box<dyn Serializer> without knowing concrete type. \
+ Use concrete type instead (e.g., HashMap<String, i32>)",
+ fory_type_id
+ )))
+ }
+ id if id == TypeId::SET as u32 => {
+ Err(Error::Other(anyhow::anyhow!(
+ "Cannot deserialize SET type ID {} as Box<dyn Serializer> without knowing concrete type. \
+ Use concrete type instead (e.g., HashSet<i32>)",
+ fory_type_id
+ )))
+ }
+ _ => {
+ Err(Error::Other(anyhow::anyhow!("Type ID {} not registered", fory_type_id)))
+ }
+ }
+ }
+ }
+
+ fn fory_read_data(_context: &mut ReadContext, _is_field: bool) -> Result<Self, Error>
+ where
+ Self: Sized + Default,
+ {
+ panic!("fory_read_data should not be called directly on Box<dyn Serializer>");
+ }
+}
+
+// Note: The macro invocations are moved to the end of the file after macro definitions
+
+/// Macro to generate serializer implementations for smart pointer types with Serializer trait
+#[macro_export]
+macro_rules! generate_smart_pointer_serializer {
+ ($pointer_type:ident, $trait_name:ident) => {
+ impl Default for $pointer_type<dyn $crate::serializer::$trait_name> {
+ fn default() -> Self {
+ panic!("{}<<dyn {}>> cannot be default-constructed", stringify!($pointer_type), stringify!($trait_name))
+ }
+ }
+
+ impl $crate::serializer::Serializer for $pointer_type<dyn $crate::serializer::$trait_name> {
+ fn fory_write(&self, context: &mut $crate::resolver::context::WriteContext, is_field: bool) {
+ use $crate::types::{Mode, RefFlag, TypeId};
+
+ context.writer.write_i8(RefFlag::NotNullValue as i8);
+
+ let fory_type_id = (**self).fory_type_id_dyn(context.get_fory());
+ context.writer.write_varuint32(fory_type_id);
+
+ if context.get_fory().get_mode() == &Mode::Compatible
+ && (fory_type_id & 0xff == TypeId::NAMED_COMPATIBLE_STRUCT as u32
+ || fory_type_id & 0xff == TypeId::COMPATIBLE_STRUCT as u32)
+ {
+ let concrete_type_id = (**self).fory_concrete_type_id();
+ let meta_index = context.push_meta(concrete_type_id) as u32;
+ context.writer.write_varuint32(meta_index);
+ }
+
+ (**self).fory_write_data(context, is_field);
+ }
+
+ fn fory_write_data(&self, _context: &mut $crate::resolver::context::WriteContext, _is_field: bool) {
+ panic!("fory_write_data should not be called directly on {}<<dyn {}>>", stringify!($pointer_type), stringify!($trait_name));
+ }
+
+ fn fory_type_id_dyn(&self, fory: &$crate::fory::Fory) -> u32 {
+ (**self).fory_type_id_dyn(fory)
+ }
+
+ fn fory_is_polymorphic() -> bool {
+ true
+ }
+
+ fn fory_write_type_info(_context: &mut $crate::resolver::context::WriteContext, _is_field: bool) {
+ // Pointer<dyn Trait> is polymorphic - type info is written per element
+ }
+
+ fn fory_read_type_info(_context: &mut $crate::resolver::context::ReadContext, _is_field: bool) {
+ // Pointer<dyn Trait> is polymorphic - type info is read per element
+ }
+
+ fn fory_read(_context: &mut $crate::resolver::context::ReadContext, _is_field: bool) -> Result<Self, $crate::error::Error>
+ where
+ Self: Sized + Default,
+ {
+ // For now, Rc and Arc deserialization is not implemented
+ // This would require complex reference counting logic
+ Err($crate::error::Error::Other($crate::error::AnyhowError::msg(
+ format!("{}<<dyn {}>> deserialization not yet implemented - use Box<dyn {}> instead",
+ stringify!($pointer_type), stringify!($trait_name), stringify!($trait_name))
+ )))
+ }
+
+ fn fory_read_data(_context: &mut $crate::resolver::context::ReadContext, _is_field: bool) -> Result<Self, $crate::error::Error>
+ where
+ Self: Sized + Default,
+ {
+ panic!("fory_read_data should not be called directly on {}<<dyn {}>>", stringify!($pointer_type), stringify!($trait_name));
+ }
+ }
+ };
+}
+
+/// Helper macros for automatic conversions in derive code
+/// These are used by fory-derive to generate transparent conversions
+///
+/// Convert field of type Rc<dyn Trait> to wrapper for serialization
+#[macro_export]
+macro_rules! wrap_rc {
+ ($field:expr, $trait_name:ident) => {
+ $crate::paste::paste! {
+ [<$trait_name Rc>]::from($field)
+ }
+ };
+}
+
+/// Convert wrapper back to Rc<dyn Trait> for deserialization
+#[macro_export]
+macro_rules! unwrap_rc {
+ ($wrapper:expr, $trait_name:ident) => {
+ std::rc::Rc::<dyn $trait_name>::from($wrapper)
+ };
+}
+
+/// Convert Arc<dyn Trait> to wrapper for serialization
+#[macro_export]
+macro_rules! wrap_arc {
+ ($field:expr, $trait_name:ident) => {
+ $crate::paste::paste! {
+ [<$trait_name Arc>]::from($field)
+ }
+ };
+}
+
+/// Convert Vec<Rc<dyn Trait>> to Vec<wrapper> for serialization
+#[macro_export]
+macro_rules! wrap_vec_rc {
+ ($vec:expr, $trait_name:ident) => {
+ $crate::paste::paste! {
+ $vec.into_iter().map(|item| [<$trait_name Rc>]::from(item)).collect()
+ }
+ };
+}
diff --git a/rust/fory-core/tests/de.rs b/rust/fory-core/tests/de.rs
deleted file mode 100644
index b248758bc..000000000
--- a/rust/fory-core/tests/de.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-// 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.
diff --git a/rust/fory-derive/src/lib.rs b/rust/fory-derive/src/lib.rs
index 2ae07ab28..e6f86ec3d 100644
--- a/rust/fory-derive/src/lib.rs
+++ b/rust/fory-derive/src/lib.rs
@@ -39,7 +39,7 @@
//! use fory_derive::Fory;
//! use std::collections::HashMap;
//!
-//! #[derive(Fory, Debug, PartialEq, Default)]
+//! #[derive(Fory, Debug, PartialEq)]
//! struct Person {
//! name: String,
//! age: i32,
@@ -48,7 +48,7 @@
//! metadata: HashMap<String, String>,
//! }
//!
-//! #[derive(Fory, Debug, PartialEq, Default)]
+//! #[derive(Fory, Debug, PartialEq)]
//! struct Address {
//! street: String,
//! city: String,
@@ -136,7 +136,7 @@
//! use fory_core::{fory::Fory, error::Error};
//! use fory_derive::Fory;
//!
-//! #[derive(Fory, Debug, PartialEq, Default)]
+//! #[derive(Fory, Debug, PartialEq)]
//! struct MyData {
//! value: i32,
//! text: String,
@@ -185,14 +185,14 @@ mod util;
/// ```rust
/// use fory_derive::Fory;
///
-/// #[derive(Fory, Debug, PartialEq, Default)]
+/// #[derive(Fory, Debug, PartialEq)]
/// struct Person {
/// name: String,
/// age: i32,
/// address: Address,
/// }
///
-/// #[derive(Fory, Debug, PartialEq, Default)]
+/// #[derive(Fory, Debug, PartialEq)]
/// struct Address {
/// street: String,
/// city: String,
@@ -201,6 +201,9 @@ mod util;
#[proc_macro_derive(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
object::derive_serializer(&input)
}
diff --git a/rust/fory-derive/src/object/misc.rs b/rust/fory-derive/src/object/misc.rs
index 3012d3aeb..ee943214c 100644
--- a/rust/fory-derive/src/object/misc.rs
+++ b/rust/fory-derive/src/object/misc.rs
@@ -20,7 +20,10 @@ use quote::quote;
use std::sync::atomic::{AtomicU32, Ordering};
use syn::Field;
-use super::util::{generic_tree_to_tokens, get_sort_fields_ts, parse_generic_tree};
+use super::util::{
+ classify_trait_object_field, generic_tree_to_tokens, get_sort_fields_ts, parse_generic_tree,
+ TraitObjectField,
+};
// Global type ID counter that auto-grows from 0 at macro processing time
static TYPE_ID_COUNTER: AtomicU32 = AtomicU32::new(0);
@@ -80,10 +83,23 @@ pub fn gen_type_def(fields: &[&Field]) -> TokenStream {
let field_infos = fields.iter().map(|field| {
let ty = &field.ty;
let name = format!("{}", field.ident.as_ref().expect("should be field name"));
- let generic_tree = parse_generic_tree(ty);
- let generic_token = generic_tree_to_tokens(&generic_tree, false);
- quote! {
- fory_core::meta::FieldInfo::new(#name, #generic_token)
+
+ match classify_trait_object_field(ty) {
+ TraitObjectField::None => {
+ let generic_tree = parse_generic_tree(ty);
+ let generic_token = generic_tree_to_tokens(&generic_tree, false);
+ quote! {
+ fory_core::meta::FieldInfo::new(#name, #generic_token)
+ }
+ }
+ _ => {
+ quote! {
+ fory_core::meta::FieldInfo::new(#name, fory_core::meta::FieldType {
+ type_id: fory_core::types::TypeId::UNKNOWN as u32,
+ generics: Vec::new()
+ })
+ }
+ }
}
});
quote! {
diff --git a/rust/fory-derive/src/object/read.rs b/rust/fory-derive/src/object/read.rs
index 6d0c4fe95..133dace90 100644
--- a/rust/fory-derive/src/object/read.rs
+++ b/rust/fory-derive/src/object/read.rs
@@ -19,7 +19,10 @@ use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use syn::{Field, Type};
-use super::util::{generic_tree_to_tokens, parse_generic_tree, NullableTypeNode};
+use super::util::{
+ classify_trait_object_field, create_wrapper_types_arc, create_wrapper_types_rc,
+ generic_tree_to_tokens, parse_generic_tree, NullableTypeNode, TraitObjectField,
+};
fn create_private_field_name(field: &Field) -> Ident {
format_ident!("_{}", field.ident.as_ref().expect(""))
@@ -35,8 +38,19 @@ fn declare_var(fields: &[&Field]) -> Vec<TokenStream> {
.map(|field| {
let ty = &field.ty;
let var_name = create_private_field_name(field);
- quote! {
- let mut #var_name: Option<#ty> = None;
+ match classify_trait_object_field(ty) {
+ TraitObjectField::BoxDyn
+ | TraitObjectField::RcDyn(_)
+ | TraitObjectField::ArcDyn(_) => {
+ quote! {
+ let mut #var_name: #ty = Default::default();
+ }
+ }
+ _ => {
+ quote! {
+ let mut #var_name: Option<#ty> = None;
+ }
+ }
}
})
.collect()
@@ -48,13 +62,137 @@ fn assign_value(fields: &[&Field]) -> Vec<TokenStream> {
.map(|field| {
let name = &field.ident;
let var_name = create_private_field_name(field);
- quote! {
- #name: #var_name.unwrap_or_default()
+ match classify_trait_object_field(&field.ty) {
+ TraitObjectField::BoxDyn
+ | TraitObjectField::RcDyn(_)
+ | TraitObjectField::ArcDyn(_) => {
+ quote! {
+ #name: #var_name
+ }
+ }
+ _ => {
+ quote! {
+ #name: #var_name.unwrap_or_default()
+ }
+ }
}
})
.collect()
}
+fn gen_read_match_arm(field: &Field, private_ident: &Ident) -> TokenStream {
+ let ty = &field.ty;
+ let name_str = field.ident.as_ref().unwrap().to_string();
+
+ match classify_trait_object_field(ty) {
+ TraitObjectField::BoxDyn => {
+ quote! {
+ #name_str => {
+ let ref_flag = context.reader.read_i8();
+ if ref_flag != fory_core::types::RefFlag::NotNullValue as i8 {
+ panic!("Expected NotNullValue for trait object field");
+ }
+
+ let fory_type_id = context.reader.read_varuint32();
+
+ let harness = context.get_fory()
+ .get_type_resolver()
+ .get_harness(fory_type_id)
+ .expect("Type not registered for trait object field");
+
+ let deserializer_fn = harness.get_deserializer();
+ let any_box = deserializer_fn(context, true, false)?;
+
+ let base_type_id = fory_type_id >> 8;
+ #private_ident = __fory_trait_helpers::from_any_internal(any_box, base_type_id)?;
+ }
+ }
+ }
+ TraitObjectField::RcDyn(trait_name) => {
+ let types = create_wrapper_types_rc(&trait_name);
+ let wrapper_ty = types.wrapper_ty;
+ let trait_ident = types.trait_ident;
+ quote! {
+ #name_str => {
+ let wrapper = <#wrapper_ty as fory_core::serializer::Serializer>::fory_read(context, true)?;
+ #private_ident = std::rc::Rc::<dyn #trait_ident>::from(wrapper);
+ }
+ }
+ }
+ TraitObjectField::ArcDyn(trait_name) => {
+ let types = create_wrapper_types_arc(&trait_name);
+ let wrapper_ty = types.wrapper_ty;
+ let trait_ident = types.trait_ident;
+ quote! {
+ #name_str => {
+ let wrapper = <#wrapper_ty as fory_core::serializer::Serializer>::fory_read(context, true)?;
+ #private_ident = std::sync::Arc::<dyn #trait_ident>::from(wrapper);
+ }
+ }
+ }
+ TraitObjectField::VecRc(trait_name) => {
+ let types = create_wrapper_types_rc(&trait_name);
+ let wrapper_ty = types.wrapper_ty;
+ let trait_ident = types.trait_ident;
+ quote! {
+ #name_str => {
+ let wrapper_vec = <Vec<#wrapper_ty> as fory_core::serializer::Serializer>::fory_read(context, true)?;
+ #private_ident = Some(wrapper_vec.into_iter()
+ .map(|w| std::rc::Rc::<dyn #trait_ident>::from(w))
+ .collect());
+ }
+ }
+ }
+ TraitObjectField::VecArc(trait_name) => {
+ let types = create_wrapper_types_arc(&trait_name);
+ let wrapper_ty = types.wrapper_ty;
+ let trait_ident = types.trait_ident;
+ quote! {
+ #name_str => {
+ let wrapper_vec = <Vec<#wrapper_ty> as fory_core::serializer::Serializer>::fory_read(context, true)?;
+ #private_ident = Some(wrapper_vec.into_iter()
+ .map(|w| std::sync::Arc::<dyn #trait_ident>::from(w))
+ .collect());
+ }
+ }
+ }
+ TraitObjectField::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! {
+ #name_str => {
+ let wrapper_map = <std::collections::HashMap<#key_ty, #wrapper_ty> as fory_core::serializer::Serializer>::fory_read(context, true)?;
+ #private_ident = Some(wrapper_map.into_iter()
+ .map(|(k, v)| (k, std::rc::Rc::<dyn #trait_ident>::from(v)))
+ .collect());
+ }
+ }
+ }
+ TraitObjectField::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! {
+ #name_str => {
+ let wrapper_map = <std::collections::HashMap<#key_ty, #wrapper_ty> as fory_core::serializer::Serializer>::fory_read(context, true)?;
+ #private_ident = Some(wrapper_map.into_iter()
+ .map(|(k, v)| (k, std::sync::Arc::<dyn #trait_ident>::from(v)))
+ .collect());
+ }
+ }
+ }
+ _ => {
+ quote! {
+ #name_str => {
+ let skip_ref_flag = fory_core::serializer::get_skip_ref_flag::<#ty>(context.get_fory());
+ #private_ident = Some(fory_core::serializer::read_ref_info_data::<#ty>(context, true, skip_ref_flag, false)?);
+ }
+ }
+ }
+ }
+}
+
pub fn gen_read_type_info() -> TokenStream {
quote! {
fory_core::serializer::struct_::read_type_info::<Self>(context, is_field)
@@ -62,14 +200,6 @@ pub fn gen_read_type_info() -> TokenStream {
}
pub fn gen_read_data(fields: &[&Field]) -> TokenStream {
- // read way before:
- // let assign_stmt = fields.iter().map(|field| {
- // let ty = &field.ty;
- // let name = &field.ident;
- // quote! {
- // #name: <#ty as fory_core::serializer::Serializer>::deserialize(context, true)?
- // }
- // });
let private_idents: Vec<Ident> = fields
.iter()
.map(|f| create_private_field_name(f))
@@ -83,20 +213,25 @@ pub fn gen_read_data(fields: &[&Field]) -> TokenStream {
.zip(private_idents.iter())
.map(|(field, private_ident)| {
let ty = &field.ty;
- quote! {
- let mut #private_ident: #ty = Default::default();
+ match classify_trait_object_field(ty) {
+ TraitObjectField::BoxDyn
+ | TraitObjectField::RcDyn(_)
+ | TraitObjectField::ArcDyn(_) => {
+ quote! {
+ let mut #private_ident: #ty = Default::default();
+ }
+ }
+ _ => {
+ quote! {
+ let mut #private_ident: Option<#ty> = None;
+ }
+ }
}
});
- let match_ts = fields.iter().zip(private_idents.iter()).map(|(field, private_ident)| {
- let ty = &field.ty;
- let name_str = field.ident.as_ref().unwrap().to_string();
- quote! {
- #name_str => {
- let skip_ref_flag = fory_core::serializer::get_skip_ref_flag::<#ty>(context.get_fory());
- #private_ident = fory_core::serializer::read_ref_info_data::<#ty>(context, true, skip_ref_flag, false)?;
- }
- }
- });
+ let match_ts = fields
+ .iter()
+ .zip(private_idents.iter())
+ .map(|(field, private_ident)| gen_read_match_arm(field, private_ident));
quote! {
#(#declare_var_ts)*
let sorted_field_names = <Self as fory_core::serializer::StructSerializer>::fory_get_sorted_field_names(context.get_fory());
@@ -113,19 +248,180 @@ pub fn gen_read_data(fields: &[&Field]) -> TokenStream {
.zip(private_idents.iter())
.map(|(field, private_ident)| {
let original_ident = &field.ident;
- quote! {
- #original_ident: #private_ident
+ let ty = &field.ty;
+ match classify_trait_object_field(ty) {
+ TraitObjectField::BoxDyn
+ | TraitObjectField::RcDyn(_)
+ | TraitObjectField::ArcDyn(_) => {
+ quote! {
+ #original_ident: #private_ident
+ }
+ }
+ _ => {
+ quote! {
+ #original_ident: #private_ident.unwrap_or_default()
+ }
+ }
}
});
quote! {
#sorted_read
Ok(Self {
#(#field_idents),*
- // #(#assign_stmt),*
})
}
}
+fn gen_read_compatible_match_arm(field: &Field, var_name: &Ident) -> TokenStream {
+ let ty = &field.ty;
+ let field_name_str = field.ident.as_ref().unwrap().to_string();
+
+ match classify_trait_object_field(ty) {
+ TraitObjectField::BoxDyn => {
+ quote! {
+ if _field.field_name.as_str() == #field_name_str {
+ let ref_flag = context.reader.read_i8();
+ if ref_flag != fory_core::types::RefFlag::NotNullValue as i8 {
+ panic!("Expected NotNullValue for trait object field");
+ }
+ let fory_type_id = context.reader.read_varuint32();
+ let harness = context.get_fory()
+ .get_type_resolver()
+ .get_harness(fory_type_id)
+ .expect("Type not registered for trait object field");
+ let deserializer_fn = harness.get_deserializer();
+ let any_box = deserializer_fn(context, true, false).unwrap();
+ let base_type_id = fory_type_id >> 8;
+ #var_name = __fory_trait_helpers::from_any_internal(any_box, base_type_id).unwrap();
+ }
+ }
+ }
+ TraitObjectField::RcDyn(trait_name) => {
+ let types = create_wrapper_types_rc(&trait_name);
+ let wrapper_ty = types.wrapper_ty;
+ let trait_ident = types.trait_ident;
+ quote! {
+ if _field.field_name.as_str() == #field_name_str {
+ let wrapper = <#wrapper_ty as fory_core::serializer::Serializer>::fory_read(context, true).unwrap();
+ #var_name = Some(std::rc::Rc::<dyn #trait_ident>::from(wrapper));
+ }
+ }
+ }
+ TraitObjectField::ArcDyn(trait_name) => {
+ let types = create_wrapper_types_arc(&trait_name);
+ let wrapper_ty = types.wrapper_ty;
+ let trait_ident = types.trait_ident;
+ quote! {
+ if _field.field_name.as_str() == #field_name_str {
+ let wrapper = <#wrapper_ty as fory_core::serializer::Serializer>::fory_read(context, true).unwrap();
+ #var_name = Some(std::sync::Arc::<dyn #trait_ident>::from(wrapper));
+ }
+ }
+ }
+ TraitObjectField::VecRc(trait_name) => {
+ let types = create_wrapper_types_rc(&trait_name);
+ let wrapper_ty = types.wrapper_ty;
+ let trait_ident = types.trait_ident;
+ quote! {
+ if _field.field_name.as_str() == #field_name_str {
+ let wrapper_vec = <Vec<#wrapper_ty> as fory_core::serializer::Serializer>::fory_read(context, true).unwrap();
+ #var_name = Some(wrapper_vec.into_iter()
+ .map(|w| std::rc::Rc::<dyn #trait_ident>::from(w))
+ .collect());
+ }
+ }
+ }
+ TraitObjectField::VecArc(trait_name) => {
+ let types = create_wrapper_types_arc(&trait_name);
+ let wrapper_ty = types.wrapper_ty;
+ let trait_ident = types.trait_ident;
+ quote! {
+ if _field.field_name.as_str() == #field_name_str {
+ let wrapper_vec = <Vec<#wrapper_ty> as fory_core::serializer::Serializer>::fory_read(context, true).unwrap();
+ #var_name = Some(wrapper_vec.into_iter()
+ .map(|w| std::sync::Arc::<dyn #trait_ident>::from(w))
+ .collect());
+ }
+ }
+ }
+ TraitObjectField::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! {
+ if _field.field_name.as_str() == #field_name_str {
+ let wrapper_map = <std::collections::HashMap<#key_ty, #wrapper_ty> as fory_core::serializer::Serializer>::fory_read(context, true).unwrap();
+ #var_name = Some(wrapper_map.into_iter()
+ .map(|(k, v)| (k, std::rc::Rc::<dyn #trait_ident>::from(v)))
+ .collect());
+ }
+ }
+ }
+ TraitObjectField::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! {
+ if _field.field_name.as_str() == #field_name_str {
+ let wrapper_map = <std::collections::HashMap<#key_ty, #wrapper_ty> as fory_core::serializer::Serializer>::fory_read(context, true).unwrap();
+ #var_name = Some(wrapper_map.into_iter()
+ .map(|(k, v)| (k, std::sync::Arc::<dyn #trait_ident>::from(v)))
+ .collect());
+ }
+ }
+ }
+ TraitObjectField::ContainsTraitObject => {
+ quote! {
+ if _field.field_name.as_str() == #field_name_str {
+ let skip_ref_flag = fory_core::serializer::get_skip_ref_flag::<#ty>(context.get_fory());
+ #var_name = Some(fory_core::serializer::read_ref_info_data::<#ty>(context, true, skip_ref_flag, false).unwrap());
+ }
+ }
+ }
+ TraitObjectField::None => {
+ let generic_tree = parse_generic_tree(ty);
+ let generic_token = generic_tree_to_tokens(&generic_tree, true);
+ let read_nullable_fn_name = create_read_nullable_fn_name(field);
+
+ let base_ty = match &ty {
+ Type::Path(type_path) => &type_path.path.segments.first().unwrap().ident,
+ _ => panic!("Unsupported type"),
+ };
+ quote! {
+ if _field.field_name.as_str() == #field_name_str {
+ let local_field_type = #generic_token;
+ if &_field.field_type == &local_field_type {
+ let skip_ref_flag = fory_core::serializer::get_skip_ref_flag::<#ty>(context.get_fory());
+ #var_name = Some(fory_core::serializer::read_ref_info_data::<#ty>(context, true, skip_ref_flag, false).unwrap_or_else(|_err| {
+ panic!("Err at deserializing {:?}: {:?}", #field_name_str, _err);
+ }));
+ } else {
+ let local_nullable_type = fory_core::meta::NullableFieldType::from(local_field_type.clone());
+ let remote_nullable_type = fory_core::meta::NullableFieldType::from(_field.field_type.clone());
+ if local_nullable_type != remote_nullable_type {
+ println!("Type not match, just skip: {}", #field_name_str);
+ let read_ref_flag = fory_core::serializer::skip::get_read_ref_flag(&remote_nullable_type);
+ fory_core::serializer::skip::skip_field_value(context, &remote_nullable_type, read_ref_flag).unwrap();
+ #var_name = Some(#base_ty::default());
+ } else {
+ println!("Try to deserialize_compatible: {}", #field_name_str);
+ #var_name = Some(
+ Self::#read_nullable_fn_name(
+ context,
+ &local_nullable_type,
+ &remote_nullable_type
+ ).unwrap_or_else(|_err| {
+ panic!("Err at deserializing {:?}: {:?}", #field_name_str, _err);
+ })
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
pub fn gen_read(struct_ident: &Ident) -> TokenStream {
quote! {
let ref_flag = context.reader.read_i8();
@@ -151,57 +447,10 @@ pub fn gen_read(struct_ident: &Ident) -> TokenStream {
}
}
-pub fn gen_read_compatible(fields: &[&Field], struct_ident: &Ident) -> TokenStream {
+pub fn gen_read_compatible(fields: &[&Field], _struct_ident: &Ident) -> TokenStream {
let pattern_items = fields.iter().map(|field| {
- let ty = &field.ty;
let var_name = create_private_field_name(field);
- let read_nullable_fn_name = create_read_nullable_fn_name(field);
-
- let generic_tree = parse_generic_tree(ty);
- // dbg!(&generic_tree);
- let generic_token = generic_tree_to_tokens(&generic_tree, true);
-
- let field_name_str = field.ident.as_ref().unwrap().to_string();
- let base_ty = match &ty {
- Type::Path(type_path) => {
- &type_path.path.segments.first().unwrap().ident
- }
- _ => panic!("Unsupported type"),
- };
- quote! {
- if _field.field_name.as_str() == #field_name_str {
- let local_field_type = #generic_token;
- if &_field.field_type == &local_field_type {
- let skip_ref_flag = fory_core::serializer::get_skip_ref_flag::<#ty>(context.get_fory());
- #var_name = Some(fory_core::serializer::read_ref_info_data::<#ty>(context, true, skip_ref_flag, false).unwrap_or_else(|_err| {
- // same type, err means something wrong
- panic!("Err at deserializing {:?}: {:?}", #field_name_str, _err);
- }));
- } else {
- let local_nullable_type = fory_core::meta::NullableFieldType::from(local_field_type.clone());
- let remote_nullable_type = fory_core::meta::NullableFieldType::from(_field.field_type.clone());
- if local_nullable_type != remote_nullable_type {
- // set default and skip bytes
- println!("Type not match, just skip: {}", #field_name_str);
- let read_ref_flag = fory_core::serializer::skip::get_read_ref_flag(&remote_nullable_type);
- fory_core::serializer::skip::skip_field_value(context, &remote_nullable_type, read_ref_flag).unwrap();
- #var_name = Some(#base_ty::default());
- } else {
- println!("Try to deserialize_compatible: {}", #field_name_str);
- #var_name = Some(
- #struct_ident::#read_nullable_fn_name(
- context,
- &local_nullable_type,
- &remote_nullable_type
- ).unwrap_or_else(|_err| {
- // same nulable type, err means something wrong
- panic!("Err at deserializing {:?}: {:?}", #field_name_str, _err);
- })
- );
- }
- }
- }
- }
+ gen_read_compatible_match_arm(field, &var_name)
});
let declare_ts: Vec<TokenStream> = declare_var(fields);
let assign_ts: Vec<TokenStream> = assign_value(fields);
@@ -231,22 +480,25 @@ pub fn gen_read_compatible(fields: &[&Field], struct_ident: &Ident) -> TokenStre
pub fn gen_read_nullable(fields: &[&Field]) -> TokenStream {
let func_tokens: Vec<TokenStream> = fields
.iter()
- .map(|field| {
- let fn_name = create_read_nullable_fn_name(field);
+ .filter_map(|field| {
let ty = &field.ty;
- let generic_tree = parse_generic_tree(ty);
- let nullable_generic_tree = NullableTypeNode::from(generic_tree);
- let read_tokens = nullable_generic_tree.to_read_tokens(&vec![], true);
- quote! {
- fn #fn_name(
- context: &mut fory_core::resolver::context::ReadContext,
- local_nullable_type: &fory_core::meta::NullableFieldType,
- remote_nullable_type: &fory_core::meta::NullableFieldType
- ) -> Result<#ty, fory_core::error::Error> {
- // println!("remote:{:#?}", remote_nullable_type);
- // println!("local:{:#?}", local_nullable_type);
- #read_tokens
+ match classify_trait_object_field(ty) {
+ TraitObjectField::None => {
+ let fn_name = create_read_nullable_fn_name(field);
+ let generic_tree = parse_generic_tree(ty);
+ let nullable_generic_tree = NullableTypeNode::from(generic_tree);
+ let read_tokens = nullable_generic_tree.to_read_tokens(&vec![], true);
+ Some(quote! {
+ fn #fn_name(
+ context: &mut fory_core::resolver::context::ReadContext,
+ local_nullable_type: &fory_core::meta::NullableFieldType,
+ remote_nullable_type: &fory_core::meta::NullableFieldType
+ ) -> Result<#ty, fory_core::error::Error> {
+ #read_tokens
+ }
+ })
}
+ _ => None,
}
})
.collect::<Vec<_>>();
diff --git a/rust/fory-derive/src/object/serializer.rs b/rust/fory-derive/src/object/serializer.rs
index 754bf4cba..a4cea60bb 100644
--- a/rust/fory-derive/src/object/serializer.rs
+++ b/rust/fory-derive/src/object/serializer.rs
@@ -19,9 +19,31 @@ use crate::object::{derive_enum, misc, read, write};
use crate::util::sorted_fields;
use proc_macro::TokenStream;
use quote::quote;
+use syn::Data;
pub fn derive_serializer(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
+
+ // Check if Default is already derived/implemented
+ let has_existing_default = ast.attrs.iter().any(|attr| {
+ attr.path().is_ident("derive") && {
+ let mut has_default = false;
+ let _ = attr.parse_nested_meta(|meta| {
+ if meta.path.is_ident("Default") {
+ has_default = true;
+ }
+ Ok(())
+ });
+ has_default
+ }
+ });
+
+ let default_impl = if !has_existing_default {
+ generate_default_impl(ast)
+ } else {
+ quote! {}
+ };
+
// StructSerializer
let (actual_type_id_ts, get_sorted_field_names_ts, type_def_ts, read_compatible_ts) =
match &ast.data {
@@ -95,6 +117,8 @@ pub fn derive_serializer(ast: &syn::DeriveInput) -> TokenStream {
let type_idx = misc::allocate_type_id();
let gen = quote! {
+ #default_impl
+
impl fory_core::serializer::StructSerializer for #name {
fn fory_type_index() -> u32 {
#type_idx
@@ -121,6 +145,14 @@ pub fn derive_serializer(ast: &syn::DeriveInput) -> TokenStream {
fory.get_type_resolver().get_type_id(&std::any::TypeId::of::<Self>(), #type_idx)
}
+ fn fory_type_id_dyn(&self, fory: &fory_core::fory::Fory) -> u32 {
+ Self::fory_get_type_id(fory)
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+
fn fory_reserved_space() -> usize {
#reserved_space_ts
}
@@ -155,3 +187,63 @@ pub fn derive_serializer(ast: &syn::DeriveInput) -> TokenStream {
};
gen.into()
}
+
+fn generate_default_impl(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
+ let name = &ast.ident;
+
+ if let Data::Struct(s) = &ast.data {
+ let fields = sorted_fields(&s.fields);
+
+ use super::util::{
+ classify_trait_object_field, create_wrapper_types_arc, create_wrapper_types_rc,
+ TraitObjectField,
+ };
+
+ let field_inits = fields.iter().map(|field| {
+ let ident = &field.ident;
+ let ty = &field.ty;
+
+ match classify_trait_object_field(ty) {
+ TraitObjectField::RcDyn(trait_name) => {
+ let types = create_wrapper_types_rc(&trait_name);
+ let wrapper_ty = types.wrapper_ty;
+ let trait_ident = types.trait_ident;
+ quote! {
+ #ident: {
+ let wrapper = #wrapper_ty::default();
+ std::rc::Rc::<dyn #trait_ident>::from(wrapper)
+ }
+ }
+ }
+ TraitObjectField::ArcDyn(trait_name) => {
+ let types = create_wrapper_types_arc(&trait_name);
+ let wrapper_ty = types.wrapper_ty;
+ let trait_ident = types.trait_ident;
+ quote! {
+ #ident: {
+ let wrapper = #wrapper_ty::default();
+ std::sync::Arc::<dyn #trait_ident>::from(wrapper)
+ }
+ }
+ }
+ _ => {
+ quote! {
+ #ident: Default::default()
+ }
+ }
+ }
+ });
+
+ return quote! {
+ impl std::default::Default for #name {
+ fn default() -> Self {
+ Self {
+ #(#field_inits),*
+ }
+ }
+ }
+ };
+ }
+
+ quote! {}
+}
diff --git a/rust/fory-derive/src/object/util.rs b/rust/fory-derive/src/object/util.rs
index 052f928c4..381dd232e 100644
--- a/rust/fory-derive/src/object/util.rs
+++ b/rust/fory-derive/src/object/util.rs
@@ -15,12 +15,100 @@
// specific language governing permissions and limitations
// under the License.
+use crate::util::{
+ detect_collection_with_trait_object, is_arc_dyn_trait, is_box_dyn_trait, is_rc_dyn_trait,
+ CollectionTraitInfo,
+};
use fory_core::types::{TypeId, BASIC_TYPE_NAMES, CONTAINER_TYPE_NAMES, PRIMITIVE_ARRAY_TYPE_MAP};
-use proc_macro2::TokenStream;
-use quote::{quote, ToTokens};
+use proc_macro2::{Ident, TokenStream};
+use quote::{format_ident, quote, ToTokens};
use std::fmt;
use syn::{parse_str, Field, GenericArgument, PathArguments, Type};
+pub(super) fn contains_trait_object(ty: &Type) -> bool {
+ match ty {
+ Type::TraitObject(_) => true,
+ Type::Path(type_path) => {
+ if is_box_dyn_trait(ty).is_some()
+ || is_rc_dyn_trait(ty).is_some()
+ || is_arc_dyn_trait(ty).is_some()
+ {
+ return true;
+ }
+
+ if let Some(seg) = type_path.path.segments.last() {
+ if let PathArguments::AngleBracketed(args) = &seg.arguments {
+ return args.args.iter().any(|arg| {
+ if let GenericArgument::Type(inner_ty) = arg {
+ contains_trait_object(inner_ty)
+ } else {
+ false
+ }
+ });
+ }
+ }
+ false
+ }
+ _ => false,
+ }
+}
+
+pub(super) struct WrapperTypes {
+ pub wrapper_ty: Ident,
+ pub trait_ident: Ident,
+}
+
+pub(super) fn create_wrapper_types_rc(trait_name: &str) -> WrapperTypes {
+ WrapperTypes {
+ wrapper_ty: format_ident!("{}Rc", trait_name),
+ trait_ident: format_ident!("{}", trait_name),
+ }
+}
+
+pub(super) fn create_wrapper_types_arc(trait_name: &str) -> WrapperTypes {
+ WrapperTypes {
+ wrapper_ty: format_ident!("{}Arc", trait_name),
+ trait_ident: format_ident!("{}", trait_name),
+ }
+}
+
+#[derive(Debug)]
+pub(super) enum TraitObjectField {
+ BoxDyn,
+ RcDyn(String),
+ ArcDyn(String),
+ VecRc(String),
+ VecArc(String),
+ HashMapRc(Box<Type>, String),
+ HashMapArc(Box<Type>, String),
+ ContainsTraitObject,
+ None,
+}
+
+pub(super) fn classify_trait_object_field(ty: &Type) -> TraitObjectField {
+ if is_box_dyn_trait(ty).is_some() {
+ return TraitObjectField::BoxDyn;
+ }
+ if let Some((_, trait_name)) = is_rc_dyn_trait(ty) {
+ return TraitObjectField::RcDyn(trait_name);
+ }
+ if let Some((_, trait_name)) = is_arc_dyn_trait(ty) {
+ return TraitObjectField::ArcDyn(trait_name);
+ }
+ if let Some(collection_info) = detect_collection_with_trait_object(ty) {
+ return match collection_info {
+ CollectionTraitInfo::VecRc(t) => TraitObjectField::VecRc(t),
+ CollectionTraitInfo::VecArc(t) => TraitObjectField::VecArc(t),
+ CollectionTraitInfo::HashMapRc(k, t) => TraitObjectField::HashMapRc(k, t),
+ CollectionTraitInfo::HashMapArc(k, t) => TraitObjectField::HashMapArc(k, t),
+ };
+ }
+ if contains_trait_object(ty) {
+ return TraitObjectField::ContainsTraitObject;
+ }
+ TraitObjectField::None
+}
+
#[derive(Debug)]
pub(super) struct TypeNode {
name: String,
@@ -501,12 +589,22 @@ impl fmt::Display for NullableTypeNode {
fn extract_type_name(ty: &Type) -> String {
if let Type::Path(type_path) = ty {
type_path.path.segments.last().unwrap().ident.to_string()
+ } else if matches!(ty, Type::TraitObject(_)) {
+ "TraitObject".to_string()
} else {
quote!(#ty).to_string()
}
}
pub(super) fn parse_generic_tree(ty: &Type) -> TypeNode {
+ // Handle trait objects specially - they can't be parsed as normal types
+ if matches!(ty, Type::TraitObject(_)) {
+ return TypeNode {
+ name: "TraitObject".to_string(),
+ generics: vec![],
+ };
+ }
+
let name = extract_type_name(ty);
let generics = if let Type::Path(type_path) = ty {
@@ -533,11 +631,16 @@ pub(super) fn parse_generic_tree(ty: &Type) -> TypeNode {
}
pub(super) fn generic_tree_to_tokens(node: &TypeNode, have_context: bool) -> TokenStream {
- if node.name == "Option" && node.generics.first().unwrap().name == "Option" {
- return quote! {
- compile_error!("adjacent Options are not supported");
- };
+ if node.name == "Option" {
+ if let Some(first_generic) = node.generics.first() {
+ if first_generic.name == "Option" {
+ return quote! {
+ compile_error!("adjacent Options are not supported");
+ };
+ }
+ }
}
+
if let Some(ts) = try_vec_of_option_primitive(node) {
return ts;
}
@@ -664,6 +767,7 @@ pub(super) fn get_sort_fields_ts(fields: &[&Field]) -> TokenStream {
};
for field in fields {
+ let ident = field.ident.as_ref().unwrap().to_string();
let ty: String = field
.ty
.to_token_stream()
@@ -671,7 +775,6 @@ pub(super) fn get_sort_fields_ts(fields: &[&Field]) -> TokenStream {
.chars()
.filter(|c| !c.is_whitespace())
.collect::<String>();
- let ident = field.ident.as_ref().unwrap().to_string();
// handle Option<Primitive> specially
if let Some(inner) = extract_option_inner(&ty) {
if PRIMITIVE_TYPE_NAMES.contains(&inner) {
@@ -687,6 +790,15 @@ pub(super) fn get_sort_fields_ts(fields: &[&Field]) -> TokenStream {
}
}
+ for field in fields {
+ if is_box_dyn_trait(&field.ty).is_some() {
+ let ident = field.ident.as_ref().unwrap().to_string();
+ if let Some(pos) = struct_or_enum_fields.iter().position(|x| x.0 == ident) {
+ struct_or_enum_fields[pos].2 = TypeId::UNKNOWN as u32;
+ }
+ }
+ }
+
fn sorter(a: &(String, String, u32), b: &(String, String, u32)) -> std::cmp::Ordering {
a.2.cmp(&b.2).then_with(|| a.0.cmp(&b.0))
}
@@ -836,12 +948,33 @@ pub(super) fn get_sort_fields_ts(fields: &[&Field]) -> TokenStream {
)
}
};
+ let trait_object_fields_ts = {
+ let trait_obj_fields: Vec<_> = struct_or_enum_fields
+ .iter()
+ .filter(|(_, _, type_id)| *type_id == fory_core::types::TypeId::UNKNOWN as u32)
+ .collect();
+
+ if trait_obj_fields.is_empty() {
+ quote! {}
+ } else {
+ let names = trait_obj_fields.iter().map(|(name, _, type_id)| {
+ quote! {
+ final_fields.push((#type_id, #name.to_string()));
+ }
+ });
+ quote! {
+ #(#names)*
+ }
+ }
+ };
+
let group_sort_enum_other_fields = {
if struct_or_enum_fields.is_empty() {
quote! {}
} else {
let ts = struct_or_enum_fields
.iter()
+ .filter(|(_, _, type_id)| *type_id != fory_core::types::TypeId::UNKNOWN as u32)
.map(|(name, ty, _)| {
let ty_type: Type = syn::parse_str(ty).unwrap();
quote! {
@@ -877,6 +1010,7 @@ pub(super) fn get_sort_fields_ts(fields: &[&Field]) -> TokenStream {
#other_declare
#container_declare
+ #trait_object_fields_ts
#group_sort_enum_other_fields
let mut sorted_field_names: Vec<String> = Vec::new();
diff --git a/rust/fory-derive/src/object/write.rs b/rust/fory-derive/src/object/write.rs
index 38e6011b5..aab92a1f0 100644
--- a/rust/fory-derive/src/object/write.rs
+++ b/rust/fory-derive/src/object/write.rs
@@ -15,6 +15,10 @@
// specific language governing permissions and limitations
// under the License.
+use super::util::{
+ classify_trait_object_field, create_wrapper_types_arc, create_wrapper_types_rc,
+ TraitObjectField,
+};
use proc_macro2::TokenStream;
use quote::quote;
use syn::Field;
@@ -22,8 +26,59 @@ use syn::Field;
pub fn gen_reserved_space(fields: &[&Field]) -> TokenStream {
let reserved_size_expr: Vec<_> = fields.iter().map(|field| {
let ty = &field.ty;
- quote! {
- <#ty as fory_core::serializer::Serializer>::fory_reserved_space() + fory_core::types::SIZE_OF_REF_AND_TYPE
+ match classify_trait_object_field(ty) {
+ TraitObjectField::BoxDyn => {
+ quote! {
+ fory_core::types::SIZE_OF_REF_AND_TYPE
+ }
+ }
+ TraitObjectField::RcDyn(trait_name) => {
+ let types = create_wrapper_types_rc(&trait_name);
+ let wrapper_ty = types.wrapper_ty;
+ quote! {
+ <#wrapper_ty as fory_core::serializer::Serializer>::fory_reserved_space() + fory_core::types::SIZE_OF_REF_AND_TYPE
+ }
+ }
+ TraitObjectField::ArcDyn(trait_name) => {
+ let types = create_wrapper_types_arc(&trait_name);
+ let wrapper_ty = types.wrapper_ty;
+ quote! {
+ <#wrapper_ty as fory_core::serializer::Serializer>::fory_reserved_space() + fory_core::types::SIZE_OF_REF_AND_TYPE
+ }
+ }
+ TraitObjectField::VecRc(trait_name) => {
+ let types = create_wrapper_types_rc(&trait_name);
+ let wrapper_ty = types.wrapper_ty;
+ quote! {
+ <Vec<#wrapper_ty> as fory_core::serializer::Serializer>::fory_reserved_space() + fory_core::types::SIZE_OF_REF_AND_TYPE
+ }
+ }
+ TraitObjectField::VecArc(trait_name) => {
+ let types = create_wrapper_types_arc(&trait_name);
+ let wrapper_ty = types.wrapper_ty;
+ quote! {
+ <Vec<#wrapper_ty> as fory_core::serializer::Serializer>::fory_reserved_space() + fory_core::types::SIZE_OF_REF_AND_TYPE
+ }
+ }
+ TraitObjectField::HashMapRc(key_ty, trait_name) => {
+ let types = create_wrapper_types_rc(&trait_name);
+ let wrapper_ty = types.wrapper_ty;
+ quote! {
+ <std::collections::HashMap<#key_ty, #wrapper_ty> as fory_core::serializer::Serializer>::fory_reserved_space() + fory_core::types::SIZE_OF_REF_AND_TYPE
+ }
+ }
+ TraitObjectField::HashMapArc(key_ty, trait_name) => {
+ let types = create_wrapper_types_arc(&trait_name);
+ let wrapper_ty = types.wrapper_ty;
+ quote! {
+ <std::collections::HashMap<#key_ty, #wrapper_ty> as fory_core::serializer::Serializer>::fory_reserved_space() + fory_core::types::SIZE_OF_REF_AND_TYPE
+ }
+ }
+ _ => {
+ quote! {
+ <#ty as fory_core::serializer::Serializer>::fory_reserved_space() + fory_core::types::SIZE_OF_REF_AND_TYPE
+ }
+ }
}
}).collect();
if reserved_size_expr.is_empty() {
@@ -39,28 +94,125 @@ pub fn gen_write_type_info() -> TokenStream {
}
}
-pub fn gen_write_data(fields: &[&Field]) -> TokenStream {
- // let accessor_expr = fields.iter().map(|field| {
- // let ty = &field.ty;
- // let ident = &field.ident;
- // quote! {
- // <#ty as fory_core::serializer::Serializer>::serialize(&self.#ident, context, true);
- // }
- // });
- let sorted_serialize = if fields.is_empty() {
- quote! {}
- } else {
- let match_ts = fields.iter().map(|field| {
- let ty = &field.ty;
- let ident = &field.ident;
- let name_str = ident.as_ref().unwrap().to_string();
+fn gen_write_match_arm(field: &Field) -> TokenStream {
+ let ty = &field.ty;
+ let ident = &field.ident;
+ let name_str = ident.as_ref().unwrap().to_string();
+
+ match classify_trait_object_field(ty) {
+ TraitObjectField::BoxDyn => {
+ quote! {
+ #name_str => {
+ let any_ref = self.#ident.as_any();
+ let concrete_type_id = any_ref.type_id();
+ let fory_type_id = context.get_fory()
+ .get_type_resolver()
+ .get_fory_type_id(concrete_type_id)
+ .expect("Type not registered for trait object field");
+
+ context.writer.write_i8(fory_core::types::RefFlag::NotNullValue as i8);
+ context.writer.write_varuint32(fory_type_id);
+
+ let harness = context.get_fory()
+ .get_type_resolver()
+ .get_harness(fory_type_id)
+ .expect("Harness not found for trait object field");
+
+ let serializer_fn = harness.get_serializer();
+ serializer_fn(any_ref, context, true);
+ }
+ }
+ }
+ TraitObjectField::RcDyn(trait_name) => {
+ let types = create_wrapper_types_rc(&trait_name);
+ let wrapper_ty = types.wrapper_ty;
+ let trait_ident = types.trait_ident;
+ quote! {
+ #name_str => {
+ let wrapper = #wrapper_ty::from(self.#ident.clone() as std::rc::Rc<dyn #trait_ident>);
+ fory_core::serializer::Serializer::fory_write(&wrapper, context, true);
+ }
+ }
+ }
+ TraitObjectField::ArcDyn(trait_name) => {
+ let types = create_wrapper_types_arc(&trait_name);
+ let wrapper_ty = types.wrapper_ty;
+ let trait_ident = types.trait_ident;
+ quote! {
+ #name_str => {
+ let wrapper = #wrapper_ty::from(self.#ident.clone() as std::sync::Arc<dyn #trait_ident>);
+ fory_core::serializer::Serializer::fory_write(&wrapper, context, true);
+ }
+ }
+ }
+ TraitObjectField::VecRc(trait_name) => {
+ let types = create_wrapper_types_rc(&trait_name);
+ let wrapper_ty = types.wrapper_ty;
+ let trait_ident = types.trait_ident;
+ quote! {
+ #name_str => {
+ let wrapper_vec: Vec<#wrapper_ty> = self.#ident.iter()
+ .map(|item| #wrapper_ty::from(item.clone() as std::rc::Rc<dyn #trait_ident>))
+ .collect();
+ fory_core::serializer::Serializer::fory_write(&wrapper_vec, context, true);
+ }
+ }
+ }
+ TraitObjectField::VecArc(trait_name) => {
+ let types = create_wrapper_types_arc(&trait_name);
+ let wrapper_ty = types.wrapper_ty;
+ let trait_ident = types.trait_ident;
+ quote! {
+ #name_str => {
+ let wrapper_vec: Vec<#wrapper_ty> = self.#ident.iter()
+ .map(|item| #wrapper_ty::from(item.clone() as std::sync::Arc<dyn #trait_ident>))
+ .collect();
+ fory_core::serializer::Serializer::fory_write(&wrapper_vec, context, true);
+ }
+ }
+ }
+ TraitObjectField::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! {
+ #name_str => {
+ let wrapper_map: std::collections::HashMap<#key_ty, #wrapper_ty> = self.#ident.iter()
+ .map(|(k, v)| (k.clone(), #wrapper_ty::from(v.clone() as std::rc::Rc<dyn #trait_ident>)))
+ .collect();
+ fory_core::serializer::Serializer::fory_write(&wrapper_map, context, true);
+ }
+ }
+ }
+ TraitObjectField::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! {
+ #name_str => {
+ let wrapper_map: std::collections::HashMap<#key_ty, #wrapper_ty> = self.#ident.iter()
+ .map(|(k, v)| (k.clone(), #wrapper_ty::from(v.clone() as std::sync::Arc<dyn #trait_ident>)))
+ .collect();
+ fory_core::serializer::Serializer::fory_write(&wrapper_map, context, true);
+ }
+ }
+ }
+ _ => {
quote! {
#name_str => {
let skip_ref_flag = fory_core::serializer::get_skip_ref_flag::<#ty>(context.get_fory());
fory_core::serializer::write_ref_info_data::<#ty>(&self.#ident, context, true, skip_ref_flag, false);
}
}
- });
+ }
+ }
+}
+
+pub fn gen_write_data(fields: &[&Field]) -> TokenStream {
+ let sorted_serialize = if fields.is_empty() {
+ quote! {}
+ } else {
+ let match_ts = fields.iter().map(|field| gen_write_match_arm(field));
quote! {
let sorted_field_names = <Self as fory_core::serializer::StructSerializer>::fory_get_sorted_field_names(context.get_fory());
for field_name in sorted_field_names {
@@ -72,9 +224,6 @@ pub fn gen_write_data(fields: &[&Field]) -> TokenStream {
}
};
quote! {
- // write way before
- // #(#accessor_expr)*
- // sort and write
#sorted_serialize
}
}
diff --git a/rust/fory-derive/src/util.rs b/rust/fory-derive/src/util.rs
index 0c4d622c7..43f7be242 100644
--- a/rust/fory-derive/src/util.rs
+++ b/rust/fory-derive/src/util.rs
@@ -15,10 +15,137 @@
// specific language governing permissions and limitations
// under the License.
-use syn::{Field, Fields};
+use syn::{Field, Fields, GenericArgument, PathArguments, Type, TypePath, TypeTraitObject};
pub fn sorted_fields(fields: &Fields) -> Vec<&Field> {
let mut fields = fields.iter().collect::<Vec<&Field>>();
fields.sort_by(|a, b| a.ident.cmp(&b.ident));
fields
}
+
+/// Check if a type is `Box<dyn Trait>` and return the trait type if it is
+pub fn is_box_dyn_trait(ty: &Type) -> Option<&TypeTraitObject> {
+ if let Type::Path(TypePath { path, .. }) = ty {
+ if let Some(seg) = path.segments.last() {
+ if seg.ident == "Box" {
+ if let PathArguments::AngleBracketed(args) = &seg.arguments {
+ if let Some(GenericArgument::Type(Type::TraitObject(trait_obj))) =
+ args.args.first()
+ {
+ return Some(trait_obj);
+ }
+ }
+ }
+ }
+ }
+ None
+}
+
+/// Check if a type is `Rc<dyn Trait>` and return the trait type and trait name if it is
+pub fn is_rc_dyn_trait(ty: &Type) -> Option<(&TypeTraitObject, String)> {
+ if let Type::Path(TypePath { path, .. }) = ty {
+ if let Some(seg) = path.segments.last() {
+ if seg.ident == "Rc" {
+ if let PathArguments::AngleBracketed(args) = &seg.arguments {
+ if let Some(GenericArgument::Type(Type::TraitObject(trait_obj))) =
+ args.args.first()
+ {
+ // Extract trait name from the trait object
+ if let Some(syn::TypeParamBound::Trait(trait_bound)) =
+ trait_obj.bounds.first()
+ {
+ if let Some(segment) = trait_bound.path.segments.last() {
+ let trait_name = segment.ident.to_string();
+ return Some((trait_obj, trait_name));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ None
+}
+
+/// Check if a type is `Arc<dyn Trait>` and return the trait type and trait name if it is
+pub fn is_arc_dyn_trait(ty: &Type) -> Option<(&TypeTraitObject, String)> {
+ if let Type::Path(TypePath { path, .. }) = ty {
+ if let Some(seg) = path.segments.last() {
+ if seg.ident == "Arc" {
+ if let PathArguments::AngleBracketed(args) = &seg.arguments {
+ if let Some(GenericArgument::Type(Type::TraitObject(trait_obj))) =
+ args.args.first()
+ {
+ // Extract trait name from the trait object
+ if let Some(syn::TypeParamBound::Trait(trait_bound)) =
+ trait_obj.bounds.first()
+ {
+ if let Some(segment) = trait_bound.path.segments.last() {
+ let trait_name = segment.ident.to_string();
+ return Some((trait_obj, trait_name));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ None
+}
+
+#[derive(Debug, Clone)]
+pub enum CollectionTraitInfo {
+ VecRc(String),
+ VecArc(String),
+ HashMapRc(Box<Type>, String),
+ HashMapArc(Box<Type>, String),
+}
+
+/// Check if a type is a collection containing Rc<dyn Trait> or Arc<dyn Trait>
+pub fn detect_collection_with_trait_object(ty: &Type) -> Option<CollectionTraitInfo> {
+ if let Type::Path(TypePath { path, .. }) = ty {
+ if let Some(seg) = path.segments.last() {
+ match seg.ident.to_string().as_str() {
+ "Vec" => {
+ if let PathArguments::AngleBracketed(args) = &seg.arguments {
+ if let Some(GenericArgument::Type(inner_ty)) = args.args.first() {
+ if let Some((_, trait_name)) = is_rc_dyn_trait(inner_ty) {
+ return Some(CollectionTraitInfo::VecRc(trait_name));
+ }
+ if let Some((_, trait_name)) = is_arc_dyn_trait(inner_ty) {
+ return Some(CollectionTraitInfo::VecArc(trait_name));
+ }
+ }
+ }
+ }
+ "HashMap" => {
+ if let PathArguments::AngleBracketed(args) = &seg.arguments {
+ let args_vec: Vec<_> = args.args.iter().collect();
+ if args_vec.len() == 2 {
+ if let (
+ GenericArgument::Type(key_ty),
+ GenericArgument::Type(value_ty),
+ ) = (args_vec[0], args_vec[1])
+ {
+ if let Some((_, trait_name)) = is_rc_dyn_trait(value_ty) {
+ return Some(CollectionTraitInfo::HashMapRc(
+ Box::new(key_ty.clone()),
+ trait_name,
+ ));
+ }
+ if let Some((_, trait_name)) = is_arc_dyn_trait(value_ty) {
+ return Some(CollectionTraitInfo::HashMapArc(
+ Box::new(key_ty.clone()),
+ trait_name,
+ ));
+ }
+ }
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+ None
+}
diff --git a/rust/fory/src/lib.rs b/rust/fory/src/lib.rs
index c240c6783..9edcf6a16 100644
--- a/rust/fory/src/lib.rs
+++ b/rust/fory/src/lib.rs
@@ -44,7 +44,7 @@
//! use fory::{Fory, Error};
//! use fory_derive::Fory;
//! use std::collections::HashMap;
-//! #[derive(Fory, Debug, PartialEq, Default)]
+//! #[derive(Fory, Debug, PartialEq)]
//! struct Person {
//! name: String,
//! age: i32,
@@ -53,7 +53,7 @@
//! metadata: HashMap<String, String>,
//! }
//!
-//! #[derive(Fory, Debug, PartialEq, Default)]
+//! #[derive(Fory, Debug, PartialEq)]
//! struct Address {
//! street: String,
//! city: String,
@@ -185,7 +185,7 @@
//! use fory_core::types::Mode;
//! use fory_derive::Fory;
//!
-//! #[derive(Fory, Debug, Default)]
+//! #[derive(Fory, Debug)]
//! struct Config {
//! name: String,
//! value: i32,
@@ -204,7 +204,7 @@
//! use fory::{Fory, Error};
//! use fory_derive::Fory;
//!
-//! #[derive(Fory, Default)]
+//! #[derive(Fory)]
//! struct Data {
//! value: i32,
//! }
@@ -248,7 +248,7 @@
//! ```rust
//! use fory_derive::{Fory, ForyRow};
//!
-//! #[derive(Fory, Default)] // For object serialization
+//! #[derive(Fory)] // For object serialization
//! #[derive(ForyRow)] // For row-based serialization
//! struct MyData {
//! field1: String,
@@ -256,4 +256,4 @@
//! }
//! ```
-pub use fory_core::{error::Error, fory::Fory, row::from_row, row::to_row};
+pub use fory_core::{error::Error, fory::Fory, register_trait_type, row::from_row, row::to_row};
diff --git a/rust/tests/tests/test_box.rs b/rust/tests/tests/test_box.rs
index ec7c53cb9..536069560 100644
--- a/rust/tests/tests/test_box.rs
+++ b/rust/tests/tests/test_box.rs
@@ -46,7 +46,7 @@ fn test_box_primitive() {
#[test]
fn test_box_struct() {
- #[derive(Fory, Debug, PartialEq, Default)]
+ #[derive(Fory, Debug, PartialEq)]
struct Person {
name: String,
age: i32,
@@ -69,7 +69,7 @@ fn test_box_struct() {
#[test]
fn test_box_struct_separate() {
- #[derive(Fory, Debug, PartialEq, Default)]
+ #[derive(Fory, Debug, PartialEq)]
struct Person {
name: String,
age: i32,
diff --git a/rust/tests/tests/test_compatible.rs b/rust/tests/tests/test_compatible.rs
index 551879c92..102831e41 100644
--- a/rust/tests/tests/test_compatible.rs
+++ b/rust/tests/tests/test_compatible.rs
@@ -23,7 +23,7 @@ use std::collections::{HashMap, HashSet};
// RUSTFLAGS="-Awarnings" cargo expand -p fory-tests --test test_compatible
#[test]
fn simple() {
- #[derive(Fory, Debug, Default)]
+ #[derive(Fory, Debug)]
struct Animal1 {
f1: HashMap<i8, Vec<i8>>,
f2: String,
@@ -34,7 +34,7 @@ fn simple() {
last: i8,
}
- #[derive(Fory, Debug, Default)]
+ #[derive(Fory, Debug)]
struct Animal2 {
f1: HashMap<i8, Vec<i8>>,
f3: Vec<i8>,
@@ -70,14 +70,14 @@ fn simple() {
#[test]
fn skip_option() {
- #[derive(Fory, Debug, Default)]
+ #[derive(Fory, Debug)]
struct Item1 {
f1: Option<i32>,
f2: Option<String>,
last: i64,
}
- #[derive(Fory, Debug, Default)]
+ #[derive(Fory, Debug)]
struct Item2 {
f1: i8,
f2: i8,
@@ -102,21 +102,21 @@ fn skip_option() {
#[test]
fn nonexistent_struct() {
- #[derive(Fory, Debug, Default)]
+ #[derive(Fory, Debug)]
pub struct Item1 {
f1: i8,
}
- #[derive(Fory, Debug, Default, PartialEq)]
+ #[derive(Fory, Debug, PartialEq)]
pub struct Item2 {
f1: i64,
}
- #[derive(Fory, Debug, Default)]
+ #[derive(Fory, Debug)]
struct Person1 {
f2: Item1,
f3: i8,
last: String,
}
- #[derive(Fory, Debug, Default)]
+ #[derive(Fory, Debug)]
struct Person2 {
f2: Item2,
f3: i64,
@@ -142,7 +142,7 @@ fn nonexistent_struct() {
#[test]
fn option() {
- #[derive(Fory, Debug, PartialEq, Default)]
+ #[derive(Fory, Debug, PartialEq)]
struct Animal {
f1: Option<String>,
f2: Option<String>,
@@ -176,7 +176,7 @@ fn nullable() {
f5: Option(None) -> Option(None)
f6: Option(None) -> value_default
*/
- #[derive(Fory, Debug, Default)]
+ #[derive(Fory, Debug)]
pub struct Item1 {
f2: i8,
f3: Option<i8>,
@@ -186,7 +186,7 @@ fn nullable() {
last: i64,
}
- #[derive(Fory, Debug, Default)]
+ #[derive(Fory, Debug)]
pub struct Item2 {
f2: Option<i8>,
f3: i8,
@@ -222,7 +222,7 @@ fn nullable() {
#[test]
fn nullable_container() {
- #[derive(Fory, Debug, Default)]
+ #[derive(Fory, Debug)]
pub struct Item1 {
f1: Vec<i8>,
f2: Option<Vec<i8>>,
@@ -236,7 +236,7 @@ fn nullable_container() {
last: i64,
}
- #[derive(Fory, Debug, Default)]
+ #[derive(Fory, Debug)]
pub struct Item2 {
f1: Option<Vec<i8>>,
f2: Vec<i8>,
@@ -285,7 +285,7 @@ fn nullable_container() {
#[test]
fn inner_nullable() {
- #[derive(Fory, Debug, Default)]
+ #[derive(Fory, Debug)]
pub struct Item1 {
f1: Vec<Option<String>>,
f2: HashSet<Option<i8>>,
@@ -293,7 +293,7 @@ fn inner_nullable() {
last: i64,
}
- #[derive(Fory, Debug, Default)]
+ #[derive(Fory, Debug)]
pub struct Item2 {
f1: Vec<String>,
f2: HashSet<i8>,
@@ -322,14 +322,14 @@ fn inner_nullable() {
#[test]
fn nullable_struct() {
- #[derive(Fory, Debug, Default, PartialEq)]
+ #[derive(Fory, Debug, PartialEq)]
pub struct Item {
name: String,
data: Vec<Option<String>>,
last: i64,
}
- #[derive(Fory, Debug, Default)]
+ #[derive(Fory, Debug)]
pub struct Person1 {
f1: Item,
f2: Option<Item>,
@@ -337,7 +337,7 @@ fn nullable_struct() {
last: i64,
}
- #[derive(Fory, Debug, Default)]
+ #[derive(Fory, Debug)]
pub struct Person2 {
f1: Option<Item>,
f2: Item,
@@ -391,7 +391,7 @@ fn enum_without_payload() {
Red,
Blue,
}
- #[derive(Fory, Debug, PartialEq, Default)]
+ #[derive(Fory, Debug, PartialEq)]
struct Person1 {
f1: Color1,
f2: Color1,
@@ -403,7 +403,7 @@ fn enum_without_payload() {
f8: Color1,
last: i8,
}
- #[derive(Fory, Debug, PartialEq, Default)]
+ #[derive(Fory, Debug, PartialEq)]
struct Person2 {
// same
f1: Color1,
@@ -458,7 +458,7 @@ fn named_enum() {
Blue,
White,
}
- #[derive(Fory, Debug, PartialEq, Default)]
+ #[derive(Fory, Debug, PartialEq)]
struct Item1 {
f1: Color,
f2: Color,
@@ -472,7 +472,7 @@ fn named_enum() {
f9: Option<Color>,
last: i8,
}
- #[derive(Fory, Debug, PartialEq, Default)]
+ #[derive(Fory, Debug, PartialEq)]
struct Item2 {
f1: Color,
f2: Option<Color>,
@@ -517,7 +517,7 @@ fn named_enum() {
#[test]
#[allow(clippy::unnecessary_literal_unwrap)]
fn boxed() {
- #[derive(Fory, Debug, PartialEq, Default)]
+ #[derive(Fory, Debug, PartialEq)]
struct Item1 {
f1: i32,
f2: i32,
@@ -527,7 +527,7 @@ fn boxed() {
f6: Option<i32>,
}
- #[derive(Fory, Debug, PartialEq, Default)]
+ #[derive(Fory, Debug, PartialEq)]
struct Item2 {
f1: i32,
f2: Option<i32>,
diff --git a/rust/tests/tests/test_complex_struct.rs b/rust/tests/tests/test_complex_struct.rs
index f572d3feb..b5b21455c 100644
--- a/rust/tests/tests/test_complex_struct.rs
+++ b/rust/tests/tests/test_complex_struct.rs
@@ -67,12 +67,12 @@ fn enum_without_payload() {
#[test]
fn complex_struct() {
- #[derive(Fory, Debug, PartialEq, Default)]
+ #[derive(Fory, Debug, PartialEq)]
struct Animal {
category: String,
}
- #[derive(Fory, Debug, PartialEq, Default)]
+ #[derive(Fory, Debug, PartialEq)]
struct Person {
// c1: Vec<u8>, // binary
c2: Vec<i16>, // primitive array
@@ -118,12 +118,12 @@ fn complex_struct() {
#[test]
fn encode_to_obin() {
- #[derive(Fory, Debug, PartialEq, Default)]
+ #[derive(Fory, Debug, PartialEq)]
struct Animal {
category: String,
}
- #[derive(Fory, Debug, PartialEq, Default)]
+ #[derive(Fory, Debug, PartialEq)]
struct Person {
f1: String,
f2: HashMap<String, i8>,
diff --git a/rust/tests/tests/test_cross_language.rs b/rust/tests/tests/test_cross_language.rs
index 71146250d..21daa199a 100644
--- a/rust/tests/tests/test_cross_language.rs
+++ b/rust/tests/tests/test_cross_language.rs
@@ -40,12 +40,12 @@ enum Color {
White,
}
-#[derive(Fory, Debug, PartialEq, Default)]
+#[derive(Fory, Debug, PartialEq)]
struct Item {
name: Option<String>,
}
-#[derive(Fory, Debug, PartialEq, Default)]
+#[derive(Fory, Debug, PartialEq)]
struct SimpleStruct {
// field_order != sorted_order
f1: HashMap<i32, f64>,
@@ -532,7 +532,7 @@ fn test_map() {
#[test]
#[ignore]
fn test_integer() {
- #[derive(Fory, Debug, PartialEq, Default)]
+ #[derive(Fory, Debug, PartialEq)]
struct Item2 {
f1: i32,
f2: Option<i32>,
diff --git a/rust/tests/tests/test_ext.rs b/rust/tests/tests/test_ext.rs
index cca4bfe5f..b7cb20b45 100644
--- a/rust/tests/tests/test_ext.rs
+++ b/rust/tests/tests/test_ext.rs
@@ -25,7 +25,7 @@ use fory_derive::Fory;
#[test]
#[allow(dead_code)]
fn test_duplicate_impl() {
- #[derive(Debug, Fory, PartialEq, Default)]
+ #[derive(Debug, Fory, PartialEq)]
struct Item1 {
f1: i32,
}
@@ -63,6 +63,14 @@ fn test_use() {
f2: 0,
})
}
+
+ fn fory_type_id_dyn(&self, fory: &Fory) -> u32 {
+ Self::fory_get_type_id(fory)
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
}
let mut fory = Fory::default().mode(Compatible).xlang(true);
let item = Item { f1: 1, f2: 2 };
diff --git a/rust/tests/tests/test_rc_arc_trait_object.rs b/rust/tests/tests/test_rc_arc_trait_object.rs
new file mode 100644
index 000000000..118b83e20
--- /dev/null
+++ b/rust/tests/tests/test_rc_arc_trait_object.rs
@@ -0,0 +1,403 @@
+// 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::Fory;
+use fory_core::register_trait_type;
+use fory_core::serializer::Serializer;
+use fory_core::types::Mode;
+use fory_core::{unwrap_rc, wrap_rc, wrap_vec_rc};
+use fory_derive::Fory;
+use std::collections::HashMap;
+use std::rc::Rc;
+use std::sync::Arc;
+
+fn fory_compatible() -> Fory {
+ Fory::default().mode(Mode::Compatible)
+}
+
+trait Animal: Serializer + Send + Sync {
+ fn speak(&self) -> String;
+ fn name(&self) -> &str;
+}
+
+#[derive(Fory, Debug, PartialEq)]
+struct Dog {
+ name: String,
+ breed: String,
+}
+
+impl Animal for Dog {
+ fn speak(&self) -> String {
+ "Woof!".to_string()
+ }
+
+ fn name(&self) -> &str {
+ &self.name
+ }
+}
+
+#[derive(Fory, Debug, PartialEq)]
+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);
+
+#[test]
+fn test_automatic_rc_wrapper_basic() {
+ // Test that wrapper types are automatically generated with trait-specific names
+ let dog_rc: Rc<dyn Animal> = Rc::new(Dog {
+ name: "Rex".to_string(),
+ breed: "Golden Retriever".to_string(),
+ });
+
+ // Convert to wrapper
+ let wrapper = AnimalRc::from(dog_rc.clone());
+
+ // Test wrapper functionality
+ assert_eq!(wrapper.as_ref().name(), "Rex");
+ assert_eq!(wrapper.as_ref().speak(), "Woof!");
+
+ // Test unwrap method (as suggested by user)
+ let unwrapped = wrapper.clone().unwrap();
+ assert_eq!(unwrapped.name(), "Rex");
+ assert_eq!(unwrapped.speak(), "Woof!");
+
+ // Convert back to Rc<dyn Animal> using From trait
+ let back_to_rc = Rc::<dyn Animal>::from(wrapper);
+ assert_eq!(back_to_rc.name(), "Rex");
+ assert_eq!(back_to_rc.speak(), "Woof!");
+}
+
+#[test]
+fn test_automatic_arc_wrapper_basic() {
+ // Test that Arc wrapper types are automatically generated with trait-specific names
+ let cat_arc: Arc<dyn Animal> = Arc::new(Cat {
+ name: "Whiskers".to_string(),
+ color: "Orange".to_string(),
+ });
+
+ // Convert to wrapper
+ let wrapper = AnimalArc::from(cat_arc.clone());
+
+ // Test wrapper functionality
+ assert_eq!(wrapper.as_ref().name(), "Whiskers");
+ assert_eq!(wrapper.as_ref().speak(), "Meow!");
+
+ // Test unwrap method (as suggested by user)
+ let unwrapped = wrapper.clone().unwrap();
+ assert_eq!(unwrapped.name(), "Whiskers");
+ assert_eq!(unwrapped.speak(), "Meow!");
+
+ // Convert back to Arc<dyn Animal> using From trait
+ let back_to_arc = Arc::<dyn Animal>::from(wrapper);
+ assert_eq!(back_to_arc.name(), "Whiskers");
+ assert_eq!(back_to_arc.speak(), "Meow!");
+}
+
+#[test]
+fn test_wrapper_polymorphism() {
+ // Test that different concrete types work through the wrapper interface
+ let dog_wrapper = AnimalRc::from(Rc::new(Dog {
+ name: "Buddy".to_string(),
+ breed: "Labrador".to_string(),
+ }) as Rc<dyn Animal>);
+
+ let cat_wrapper = AnimalRc::from(Rc::new(Cat {
+ name: "Mittens".to_string(),
+ color: "Gray".to_string(),
+ }) as Rc<dyn Animal>);
+
+ // Test that both wrappers work correctly with polymorphism
+ assert_eq!(dog_wrapper.as_ref().name(), "Buddy");
+ assert_eq!(dog_wrapper.as_ref().speak(), "Woof!");
+
+ assert_eq!(cat_wrapper.as_ref().name(), "Mittens");
+ assert_eq!(cat_wrapper.as_ref().speak(), "Meow!");
+
+ // Test conversion back to trait objects
+ let dog_back = dog_wrapper.unwrap();
+ let cat_back = cat_wrapper.unwrap();
+
+ assert_eq!(dog_back.name(), "Buddy");
+ assert_eq!(dog_back.speak(), "Woof!");
+ assert_eq!(cat_back.name(), "Mittens");
+ assert_eq!(cat_back.speak(), "Meow!");
+}
+
+#[test]
+fn test_wrapper_default_implementations() {
+ // Test that wrapper types have proper Default implementations
+ let default_rc = AnimalRc::default();
+ // Dog::default() should have empty name
+ assert_eq!(default_rc.as_ref().name(), "");
+
+ let default_arc = AnimalArc::default();
+ // Dog::default() should have empty name
+ assert_eq!(default_arc.as_ref().name(), "");
+}
+
+#[test]
+fn test_wrapper_debug_formatting() {
+ let dog_wrapper = AnimalRc::from(Rc::new(Dog {
+ name: "Rex".to_string(),
+ breed: "Golden Retriever".to_string(),
+ }) as Rc<dyn Animal>);
+
+ let debug_string = format!("{:?}", dog_wrapper);
+ println!("Debug string: {}", debug_string);
+ // Debug shows memory address, not content - this is expected for trait objects
+ assert!(debug_string.contains("Animal")); // Debug should show the trait name
+}
+
+#[test]
+fn test_conversion_helper_macros() {
+ // Test the conversion helper macros that would be used by derive code
+ let dog_rc: Rc<dyn Animal> = Rc::new(Dog {
+ name: "Rex".to_string(),
+ breed: "Golden Retriever".to_string(),
+ });
+
+ // Test wrap_rc! macro
+ let wrapper = wrap_rc!(dog_rc.clone(), Animal);
+ assert_eq!(wrapper.as_ref().name(), "Rex");
+
+ // Test unwrap_rc! macro
+ let back_to_rc = unwrap_rc!(wrapper, Animal);
+ assert_eq!(back_to_rc.name(), "Rex");
+
+ // Test collection conversion macros
+ let animals = vec![
+ Rc::new(Dog {
+ name: "Dog1".to_string(),
+ breed: "Breed1".to_string(),
+ }) as Rc<dyn Animal>,
+ Rc::new(Cat {
+ name: "Cat1".to_string(),
+ color: "Color1".to_string(),
+ }) as Rc<dyn Animal>,
+ ];
+
+ let wrapped_animals: Vec<AnimalRc> = wrap_vec_rc!(animals.clone(), Animal);
+ assert_eq!(wrapped_animals.len(), 2);
+ assert_eq!(wrapped_animals[0].as_ref().name(), "Dog1");
+ assert_eq!(wrapped_animals[1].as_ref().name(), "Cat1");
+
+ let back_to_animals: Vec<Rc<dyn Animal>> = wrapped_animals
+ .into_iter()
+ .map(Rc::<dyn Animal>::from)
+ .collect();
+ assert_eq!(back_to_animals.len(), 2);
+ assert_eq!(back_to_animals[0].name(), "Dog1");
+ assert_eq!(back_to_animals[1].name(), "Cat1");
+}
+
+#[test]
+fn test_nested_wrapper_collections() {
+ let mut fory = fory_compatible();
+ fory.register::<Dog>(100);
+ fory.register::<Cat>(101);
+
+ // Wrapper types are not registered since they're transparent
+
+ // First test simple Vec<AnimalRc> to isolate the issue
+ let simple_wrappers: Vec<AnimalRc> = vec![
+ AnimalRc::from(Rc::new(Dog {
+ name: "Dog1".to_string(),
+ breed: "Breed1".to_string(),
+ }) as Rc<dyn Animal>),
+ AnimalRc::from(Rc::new(Cat {
+ name: "Cat1".to_string(),
+ color: "Color1".to_string(),
+ }) as Rc<dyn Animal>),
+ ];
+ let serialized_simple = fory.serialize(&simple_wrappers);
+ let deserialized_simple: Vec<AnimalRc> = fory.deserialize(&serialized_simple).unwrap();
+
+ assert_eq!(deserialized_simple.len(), 2);
+ assert_eq!(deserialized_simple[0].as_ref().name(), "Dog1");
+ assert_eq!(deserialized_simple[1].as_ref().name(), "Cat1");
+
+ // Test Vec<Vec<AnimalRc>>
+ let nested_wrappers: Vec<Vec<AnimalRc>> = vec![
+ vec![
+ AnimalRc::from(Rc::new(Dog {
+ name: "Dog1".to_string(),
+ breed: "Breed1".to_string(),
+ }) as Rc<dyn Animal>),
+ AnimalRc::from(Rc::new(Cat {
+ name: "Cat1".to_string(),
+ color: "Color1".to_string(),
+ }) as Rc<dyn Animal>),
+ ],
+ vec![AnimalRc::from(Rc::new(Dog {
+ name: "Dog2".to_string(),
+ breed: "Breed2".to_string(),
+ }) as Rc<dyn Animal>)],
+ ];
+
+ let serialized = fory.serialize(&nested_wrappers);
+ let deserialized: Vec<Vec<AnimalRc>> = fory.deserialize(&serialized).unwrap();
+
+ assert_eq!(deserialized.len(), 2);
+ assert_eq!(deserialized[0].len(), 2);
+ assert_eq!(deserialized[1].len(), 1);
+ assert_eq!(deserialized[0][0].as_ref().name(), "Dog1");
+ assert_eq!(deserialized[0][0].as_ref().speak(), "Woof!");
+ assert_eq!(deserialized[0][1].as_ref().name(), "Cat1");
+ assert_eq!(deserialized[0][1].as_ref().speak(), "Meow!");
+ assert_eq!(deserialized[1][0].as_ref().name(), "Dog2");
+ assert_eq!(deserialized[1][0].as_ref().speak(), "Woof!");
+}
+
+#[test]
+fn test_empty_wrapper_collections() {
+ let mut fory = fory_compatible();
+ fory.register::<Dog>(8001);
+ fory.register::<Cat>(8002);
+
+ // Test empty collections
+ let empty_rc_vec: Vec<AnimalRc> = vec![];
+ let serialized = fory.serialize(&empty_rc_vec);
+ let deserialized: Vec<AnimalRc> = fory.deserialize(&serialized).unwrap();
+ assert_eq!(deserialized.len(), 0);
+
+ let empty_arc_vec: Vec<AnimalArc> = vec![];
+ let serialized = fory.serialize(&empty_arc_vec);
+ let deserialized: Vec<AnimalArc> = fory.deserialize(&serialized).unwrap();
+ assert_eq!(deserialized.len(), 0);
+
+ let empty_map: HashMap<String, AnimalRc> = HashMap::new();
+ let serialized = fory.serialize(&empty_map);
+ let deserialized: HashMap<String, AnimalRc> = fory.deserialize(&serialized).unwrap();
+ assert_eq!(deserialized.len(), 0);
+}
+
+#[derive(Fory)]
+struct AnimalShelter {
+ animals_rc: Vec<Rc<dyn Animal>>,
+ animals_arc: Vec<Arc<dyn Animal>>,
+ animal_registry: HashMap<String, Arc<dyn Animal>>,
+}
+
+#[test]
+fn test_collections_of_wrappers() {
+ let mut fory = fory_compatible();
+ fory.register::<Dog>(8001);
+ fory.register::<Cat>(8002);
+ fory.register::<AnimalShelter>(8011);
+
+ let shelter = AnimalShelter {
+ animals_rc: vec![
+ Rc::new(Dog {
+ name: "Rex".to_string(),
+ breed: "Golden Retriever".to_string(),
+ }) as Rc<dyn Animal>,
+ Rc::new(Cat {
+ name: "Mittens".to_string(),
+ color: "Gray".to_string(),
+ }) as Rc<dyn Animal>,
+ ],
+ animals_arc: vec![Arc::new(Dog {
+ name: "Buddy".to_string(),
+ breed: "Labrador".to_string(),
+ }) as Arc<dyn Animal>],
+ animal_registry: {
+ let mut map: HashMap<String, Arc<dyn Animal>> = HashMap::new();
+ map.insert(
+ "pet1".to_string(),
+ Arc::new(Dog {
+ name: "Max".to_string(),
+ breed: "German Shepherd".to_string(),
+ }) as Arc<dyn Animal>,
+ );
+ map.insert(
+ "pet2".to_string(),
+ Arc::new(Cat {
+ name: "Luna".to_string(),
+ color: "Black".to_string(),
+ }) as Arc<dyn Animal>,
+ );
+ map
+ },
+ };
+
+ let serialized = fory.serialize(&shelter);
+ let deserialized: AnimalShelter = fory.deserialize(&serialized).unwrap();
+
+ // Test Vec<AnimalRc>
+ assert_eq!(deserialized.animals_rc.len(), 2);
+ assert_eq!(deserialized.animals_rc[0].as_ref().name(), "Rex");
+ assert_eq!(deserialized.animals_rc[0].as_ref().speak(), "Woof!");
+ assert_eq!(deserialized.animals_rc[1].as_ref().name(), "Mittens");
+ assert_eq!(deserialized.animals_rc[1].as_ref().speak(), "Meow!");
+
+ // Test Vec<AnimalArc>
+ assert_eq!(deserialized.animals_arc.len(), 1);
+ assert_eq!(deserialized.animals_arc[0].as_ref().name(), "Buddy");
+ assert_eq!(deserialized.animals_arc[0].as_ref().speak(), "Woof!");
+
+ // Test HashMap<String, AnimalRc>
+ assert_eq!(deserialized.animal_registry.len(), 2);
+ assert_eq!(
+ deserialized
+ .animal_registry
+ .get("pet1")
+ .unwrap()
+ .as_ref()
+ .name(),
+ "Max"
+ );
+ assert_eq!(
+ deserialized
+ .animal_registry
+ .get("pet1")
+ .unwrap()
+ .as_ref()
+ .speak(),
+ "Woof!"
+ );
+ assert_eq!(
+ deserialized
+ .animal_registry
+ .get("pet2")
+ .unwrap()
+ .as_ref()
+ .name(),
+ "Luna"
+ );
+ assert_eq!(
+ deserialized
+ .animal_registry
+ .get("pet2")
+ .unwrap()
+ .as_ref()
+ .speak(),
+ "Meow!"
+ );
+}
diff --git a/rust/tests/tests/test_trait_object.rs b/rust/tests/tests/test_trait_object.rs
new file mode 100644
index 000000000..628055303
--- /dev/null
+++ b/rust/tests/tests/test_trait_object.rs
@@ -0,0 +1,564 @@
+// 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::Fory;
+use fory_core::register_trait_type;
+use fory_core::serializer::Serializer;
+use fory_core::types::Mode;
+use fory_derive::Fory;
+use std::collections::{HashMap, HashSet};
+
+fn fory_compatible() -> Fory {
+ Fory::default().mode(Mode::Compatible)
+}
+
+#[test]
+fn test_multiple_types_in_sequence() {
+ let fory = fory_compatible();
+
+ let original1 = 42i32;
+ let original2 = String::from("test");
+ let original3 = vec![1, 2, 3];
+
+ let val1: Box<dyn Serializer> = Box::new(original1);
+ let val2: Box<dyn Serializer> = Box::new(original2.clone());
+ let val3: Box<dyn Serializer> = Box::new(original3.clone());
+
+ let ser1 = fory.serialize(&val1);
+ let ser2 = fory.serialize(&val2);
+ let ser3 = fory.serialize(&val3);
+
+ let de1_trait: Box<dyn Serializer> = fory.deserialize(&ser1).unwrap();
+ let de2_trait: Box<dyn Serializer> = fory.deserialize(&ser2).unwrap();
+ let de3_trait: Box<dyn Serializer> = fory.deserialize(&ser3).unwrap();
+
+ let de1_concrete: i32 = fory.deserialize(&ser1).unwrap();
+ let de2_concrete: String = fory.deserialize(&ser2).unwrap();
+ let de3_concrete: Vec<i32> = fory.deserialize(&ser3).unwrap();
+
+ assert_eq!(de1_concrete, original1);
+ assert_eq!(de2_concrete, original2);
+ assert_eq!(de3_concrete, original3);
+
+ assert_eq!(ser1, fory.serialize(&de1_trait));
+ assert_eq!(ser2, fory.serialize(&de2_trait));
+ assert_eq!(ser3, fory.serialize(&de3_trait));
+}
+
+#[test]
+fn test_option_some_roundtrip() {
+ let fory = fory_compatible();
+
+ let original = Some(42);
+ let trait_obj: Box<dyn Serializer> = Box::new(original);
+ let serialized = fory.serialize(&trait_obj);
+
+ let deserialized_trait: Box<dyn Serializer> = fory.deserialize(&serialized).unwrap();
+ let deserialized_concrete: Option<i32> = fory.deserialize(&serialized).unwrap();
+
+ assert_eq!(deserialized_concrete, original);
+ assert_eq!(fory.serialize(&deserialized_trait), serialized);
+}
+
+#[test]
+fn test_hashmap_roundtrip() {
+ let mut fory = fory_compatible();
+ fory.register_serializer::<HashMap<String, i32>>(1001);
+
+ let mut original = HashMap::new();
+ original.insert(String::from("one"), 1);
+ original.insert(String::from("two"), 2);
+ original.insert(String::from("three"), 3);
+
+ let trait_obj: Box<dyn Serializer> = Box::new(original.clone());
+ let serialized = fory.serialize(&trait_obj);
+
+ let deserialized_concrete: HashMap<String, i32> = fory.deserialize(&serialized).unwrap();
+
+ assert_eq!(deserialized_concrete.len(), 3);
+ assert_eq!(deserialized_concrete.get("one"), Some(&1));
+ assert_eq!(deserialized_concrete.get("two"), Some(&2));
+ assert_eq!(deserialized_concrete.get("three"), Some(&3));
+}
+
+#[test]
+fn test_hashset_roundtrip() {
+ let mut fory = fory_compatible();
+ fory.register_serializer::<HashSet<i32>>(1002);
+
+ let mut original = HashSet::new();
+ original.insert(1);
+ original.insert(2);
+ original.insert(3);
+
+ let trait_obj: Box<dyn Serializer> = Box::new(original.clone());
+ let serialized = fory.serialize(&trait_obj);
+
+ let deserialized_concrete: HashSet<i32> = fory.deserialize(&serialized).unwrap();
+
+ assert_eq!(deserialized_concrete.len(), 3);
+ assert!(deserialized_concrete.contains(&1));
+ assert!(deserialized_concrete.contains(&2));
+ assert!(deserialized_concrete.contains(&3));
+}
+
+#[test]
+fn test_vec_of_trait_objects() {
+ let mut fory = fory_compatible();
+ fory.register_serializer::<Vec<Box<dyn Serializer>>>(3000);
+
+ let vec_of_trait_objects: Vec<Box<dyn Serializer>> = vec![
+ Box::new(42i32),
+ Box::new(String::from("hello")),
+ Box::new(2.71f64),
+ ];
+
+ let serialized = fory.serialize(&vec_of_trait_objects);
+ let deserialized: Vec<Box<dyn Serializer>> = fory.deserialize(&serialized).unwrap();
+
+ assert_eq!(deserialized.len(), 3);
+}
+
+#[test]
+fn test_hashmap_string_to_trait_objects() {
+ let mut fory = fory_compatible();
+ fory.register_serializer::<HashMap<String, Box<dyn Serializer>>>(3002);
+
+ let mut map: HashMap<String, Box<dyn Serializer>> = HashMap::new();
+ map.insert(String::from("int"), Box::new(42i32));
+ map.insert(String::from("string"), Box::new(String::from("hello")));
+ map.insert(String::from("float"), Box::new(2.71f64));
+
+ let serialized = fory.serialize(&map);
+ let deserialized: HashMap<String, Box<dyn Serializer>> = fory.deserialize(&serialized).unwrap();
+
+ assert_eq!(deserialized.len(), 3);
+}
+
+#[derive(Fory, Debug, PartialEq, Clone)]
+struct Person {
+ name: String,
+ age: i32,
+}
+
+#[derive(Fory, Debug, PartialEq)]
+struct Company {
+ name: String,
+ employees: Vec<Person>,
+}
+
+#[test]
+fn test_fory_derived_struct_as_trait_object() {
+ let mut fory = fory_compatible();
+ fory.register::<Person>(5000);
+
+ let person = Person {
+ name: String::from("Alice"),
+ age: 30,
+ };
+ let trait_obj: Box<dyn Serializer> = Box::new(person.clone());
+ let serialized = fory.serialize(&trait_obj);
+
+ let deserialized_trait: Box<dyn Serializer> = fory.deserialize(&serialized).unwrap();
+ let deserialized_concrete: Person = fory.deserialize(&serialized).unwrap();
+
+ assert_eq!(deserialized_concrete.name, person.name);
+ assert_eq!(deserialized_concrete.age, person.age);
+
+ let reserialized = fory.serialize(&deserialized_trait);
+ assert_eq!(serialized, reserialized);
+}
+
+#[test]
+fn test_vec_of_fory_derived_trait_objects() {
+ let mut fory = fory_compatible();
+ fory.register::<Person>(5000);
+ fory.register::<Company>(5001);
+ fory.register_serializer::<Vec<Box<dyn Serializer>>>(3000);
+
+ let vec_of_trait_objects: Vec<Box<dyn Serializer>> = vec![
+ Box::new(Person {
+ name: String::from("Alice"),
+ age: 30,
+ }),
+ Box::new(Company {
+ name: String::from("Acme"),
+ employees: vec![Person {
+ name: String::from("Bob"),
+ age: 25,
+ }],
+ }),
+ Box::new(42i32),
+ ];
+
+ let serialized = fory.serialize(&vec_of_trait_objects);
+ let deserialized: Vec<Box<dyn Serializer>> = fory.deserialize(&serialized).unwrap();
+
+ assert_eq!(deserialized.len(), 3);
+}
+
+#[test]
+fn test_hashmap_with_fory_derived_values() {
+ let mut fory = fory_compatible();
+ fory.register::<Person>(5000);
+ fory.register::<Company>(5001);
+ fory.register_serializer::<HashMap<String, Box<dyn Serializer>>>(3002);
+
+ let mut map: HashMap<String, Box<dyn Serializer>> = HashMap::new();
+ map.insert(
+ String::from("person"),
+ Box::new(Person {
+ name: String::from("Alice"),
+ age: 30,
+ }),
+ );
+ map.insert(
+ String::from("company"),
+ Box::new(Company {
+ name: String::from("Acme"),
+ employees: vec![],
+ }),
+ );
+ map.insert(String::from("number"), Box::new(42i32));
+
+ let serialized = fory.serialize(&map);
+ let deserialized: HashMap<String, Box<dyn Serializer>> = fory.deserialize(&serialized).unwrap();
+
+ assert_eq!(deserialized.len(), 3);
+}
+
+// Tests for custom trait objects (Box<dyn CustomTrait>)
+
+trait Animal: Serializer {
+ fn speak(&self) -> String;
+ fn name(&self) -> &str;
+}
+
+#[derive(Fory, Debug, PartialEq)]
+struct Dog {
+ name: String,
+ breed: String,
+}
+
+impl Animal for Dog {
+ fn speak(&self) -> String {
+ "Woof!".to_string()
+ }
+
+ fn name(&self) -> &str {
+ &self.name
+ }
+}
+
+#[derive(Fory, Debug, PartialEq)]
+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(Fory)]
+struct Zoo {
+ star_animal: Box<dyn Animal>,
+}
+
+#[test]
+fn test_custom_trait_object_basic() {
+ let mut fory = fory_compatible();
+ fory.register::<Dog>(8001);
+ fory.register::<Cat>(8002);
+ fory.register::<Zoo>(8003);
+
+ let zoo_dog = Zoo {
+ star_animal: Box::new(Dog {
+ name: "Buddy".to_string(),
+ breed: "Labrador".to_string(),
+ }),
+ };
+
+ let zoo_cat = Zoo {
+ star_animal: Box::new(Cat {
+ name: "Whiskers".to_string(),
+ color: "Orange".to_string(),
+ }),
+ };
+
+ let serialized_dog = fory.serialize(&zoo_dog);
+ let serialized_cat = fory.serialize(&zoo_cat);
+
+ let deserialized_dog: Zoo = fory.deserialize(&serialized_dog).unwrap();
+ let deserialized_cat: Zoo = fory.deserialize(&serialized_cat).unwrap();
+
+ assert_eq!(deserialized_dog.star_animal.name(), "Buddy");
+ assert_eq!(deserialized_dog.star_animal.speak(), "Woof!");
+
+ assert_eq!(deserialized_cat.star_animal.name(), "Whiskers");
+ assert_eq!(deserialized_cat.star_animal.speak(), "Meow!");
+}
+
+// Tests for direct Vec<Box<dyn CustomTrait>> and HashMap<String, Box<dyn CustomTrait>>
+// These should work automatically now with the enhanced register_trait_type! macro
+
+#[test]
+fn test_single_custom_trait_object_direct() {
+ let mut fory = fory_compatible();
+ fory.register::<Dog>(8001);
+ fory.register::<Cat>(8002);
+
+ let animal: Box<dyn Animal> = Box::new(Dog {
+ name: "Rex".to_string(),
+ breed: "Golden Retriever".to_string(),
+ });
+
+ let serialized = fory.serialize(&animal);
+ let deserialized: Box<dyn Animal> = fory.deserialize(&serialized).unwrap();
+
+ assert_eq!(deserialized.name(), "Rex");
+ assert_eq!(deserialized.speak(), "Woof!");
+}
+
+#[test]
+fn test_vec_custom_trait_objects_direct() {
+ let mut fory = fory_compatible();
+ fory.register::<Dog>(8001);
+ fory.register::<Cat>(8002);
+
+ let animals: Vec<Box<dyn Animal>> = vec![
+ Box::new(Dog {
+ name: "Rex".to_string(),
+ breed: "Golden Retriever".to_string(),
+ }),
+ Box::new(Cat {
+ name: "Whiskers".to_string(),
+ color: "Orange".to_string(),
+ }),
+ Box::new(Dog {
+ name: "Buddy".to_string(),
+ breed: "Labrador".to_string(),
+ }),
+ ];
+
+ let serialized = fory.serialize(&animals);
+ let deserialized: Vec<Box<dyn Animal>> = fory.deserialize(&serialized).unwrap();
+
+ assert_eq!(deserialized.len(), 3);
+ assert_eq!(deserialized[0].name(), "Rex");
+ assert_eq!(deserialized[0].speak(), "Woof!");
+ assert_eq!(deserialized[1].name(), "Whiskers");
+ assert_eq!(deserialized[1].speak(), "Meow!");
+ assert_eq!(deserialized[2].name(), "Buddy");
+ assert_eq!(deserialized[2].speak(), "Woof!");
+}
+
+#[test]
+fn test_hashmap_custom_trait_objects_direct() {
+ let mut fory = fory_compatible();
+ fory.register::<Dog>(8001);
+ fory.register::<Cat>(8002);
+
+ let mut animal_map: std::collections::HashMap<String, Box<dyn Animal>> =
+ std::collections::HashMap::new();
+ animal_map.insert(
+ "pet1".to_string(),
+ Box::new(Dog {
+ name: "Max".to_string(),
+ breed: "German Shepherd".to_string(),
+ }),
+ );
+ animal_map.insert(
+ "pet2".to_string(),
+ Box::new(Cat {
+ name: "Luna".to_string(),
+ color: "Black".to_string(),
+ }),
+ );
+ animal_map.insert(
+ "pet3".to_string(),
+ Box::new(Dog {
+ name: "Charlie".to_string(),
+ breed: "Beagle".to_string(),
+ }),
+ );
+
+ let serialized = fory.serialize(&animal_map);
+ let deserialized: std::collections::HashMap<String, Box<dyn Animal>> =
+ fory.deserialize(&serialized).unwrap();
+
+ assert_eq!(deserialized.len(), 3);
+ assert_eq!(deserialized.get("pet1").unwrap().name(), "Max");
+ assert_eq!(deserialized.get("pet1").unwrap().speak(), "Woof!");
+ assert_eq!(deserialized.get("pet2").unwrap().name(), "Luna");
+ assert_eq!(deserialized.get("pet2").unwrap().speak(), "Meow!");
+ assert_eq!(deserialized.get("pet3").unwrap().name(), "Charlie");
+ assert_eq!(deserialized.get("pet3").unwrap().speak(), "Woof!");
+}
+
+#[test]
+fn test_nested_custom_trait_object_collections() {
+ let mut fory = fory_compatible();
+ fory.register::<Dog>(8001);
+ fory.register::<Cat>(8002);
+
+ // Test Vec<Vec<Box<dyn Animal>>>
+ let nested_animals: Vec<Vec<Box<dyn Animal>>> = vec![
+ vec![
+ Box::new(Dog {
+ name: "Dog1".to_string(),
+ breed: "Breed1".to_string(),
+ }),
+ Box::new(Cat {
+ name: "Cat1".to_string(),
+ color: "Color1".to_string(),
+ }),
+ ],
+ vec![Box::new(Dog {
+ name: "Dog2".to_string(),
+ breed: "Breed2".to_string(),
+ })],
+ ];
+
+ let serialized = fory.serialize(&nested_animals);
+ let deserialized: Vec<Vec<Box<dyn Animal>>> = fory.deserialize(&serialized).unwrap();
+
+ assert_eq!(deserialized.len(), 2);
+ assert_eq!(deserialized[0].len(), 2);
+ assert_eq!(deserialized[1].len(), 1);
+ assert_eq!(deserialized[0][0].name(), "Dog1");
+ assert_eq!(deserialized[0][0].speak(), "Woof!");
+ assert_eq!(deserialized[0][1].name(), "Cat1");
+ assert_eq!(deserialized[0][1].speak(), "Meow!");
+ assert_eq!(deserialized[1][0].name(), "Dog2");
+ assert_eq!(deserialized[1][0].speak(), "Woof!");
+}
+
+#[test]
+fn test_mixed_trait_object_collections() {
+ let mut fory = fory_compatible();
+ fory.register::<Dog>(8001);
+ fory.register::<Cat>(8002);
+
+ // Test HashMap<String, Vec<Box<dyn Animal>>>
+ let mut groups: std::collections::HashMap<String, Vec<Box<dyn Animal>>> =
+ std::collections::HashMap::new();
+
+ groups.insert(
+ "dogs".to_string(),
+ vec![
+ Box::new(Dog {
+ name: "Rex".to_string(),
+ breed: "Husky".to_string(),
+ }),
+ Box::new(Dog {
+ name: "Max".to_string(),
+ breed: "Poodle".to_string(),
+ }),
+ ],
+ );
+
+ groups.insert(
+ "cats".to_string(),
+ vec![Box::new(Cat {
+ name: "Fluffy".to_string(),
+ color: "White".to_string(),
+ })],
+ );
+
+ let serialized = fory.serialize(&groups);
+ let deserialized: std::collections::HashMap<String, Vec<Box<dyn Animal>>> =
+ fory.deserialize(&serialized).unwrap();
+
+ assert_eq!(deserialized.len(), 2);
+
+ let dogs = deserialized.get("dogs").unwrap();
+ assert_eq!(dogs.len(), 2);
+ assert_eq!(dogs[0].name(), "Rex");
+ assert_eq!(dogs[0].speak(), "Woof!");
+ assert_eq!(dogs[1].name(), "Max");
+ assert_eq!(dogs[1].speak(), "Woof!");
+
+ let cats = deserialized.get("cats").unwrap();
+ assert_eq!(cats.len(), 1);
+ assert_eq!(cats[0].name(), "Fluffy");
+ assert_eq!(cats[0].speak(), "Meow!");
+}
+
+#[test]
+fn test_empty_trait_object_collections() {
+ let mut fory = fory_compatible();
+ fory.register::<Dog>(8001);
+ fory.register::<Cat>(8002);
+
+ // Test empty Vec<Box<dyn Animal>>
+ let empty_animals: Vec<Box<dyn Animal>> = vec![];
+ let serialized = fory.serialize(&empty_animals);
+ let deserialized: Vec<Box<dyn Animal>> = fory.deserialize(&serialized).unwrap();
+ assert_eq!(deserialized.len(), 0);
+
+ // Test empty HashMap<String, Box<dyn Animal>>
+ let empty_map: std::collections::HashMap<String, Box<dyn Animal>> =
+ std::collections::HashMap::new();
+ let serialized = fory.serialize(&empty_map);
+ let deserialized: std::collections::HashMap<String, Box<dyn Animal>> =
+ fory.deserialize(&serialized).unwrap();
+ assert_eq!(deserialized.len(), 0);
+}
+
+#[test]
+fn test_single_item_trait_object_collections() {
+ let mut fory = fory_compatible();
+ fory.register::<Dog>(8001);
+ fory.register::<Cat>(8002);
+
+ // Test single item Vec<Box<dyn Animal>>
+ let single_animal: Vec<Box<dyn Animal>> = vec![Box::new(Dog {
+ name: "Solo".to_string(),
+ breed: "Bulldog".to_string(),
+ })];
+ let serialized = fory.serialize(&single_animal);
+ let deserialized: Vec<Box<dyn Animal>> = fory.deserialize(&serialized).unwrap();
+ assert_eq!(deserialized.len(), 1);
+ assert_eq!(deserialized[0].name(), "Solo");
+ assert_eq!(deserialized[0].speak(), "Woof!");
+
+ // Test single item HashMap<String, Box<dyn Animal>>
+ let mut single_map: std::collections::HashMap<String, Box<dyn Animal>> =
+ std::collections::HashMap::new();
+ single_map.insert(
+ "only_pet".to_string(),
+ Box::new(Cat {
+ name: "Loner".to_string(),
+ color: "Gray".to_string(),
+ }),
+ );
+ let serialized = fory.serialize(&single_map);
+ let deserialized: std::collections::HashMap<String, Box<dyn Animal>> =
+ fory.deserialize(&serialized).unwrap();
+ assert_eq!(deserialized.len(), 1);
+ assert_eq!(deserialized.get("only_pet").unwrap().name(), "Loner");
+ assert_eq!(deserialized.get("only_pet").unwrap().speak(), "Meow!");
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@fory.apache.org
For additional commands, e-mail: commits-help@fory.apache.org