| // 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 super::field_meta::parse_field_meta; |
| use super::util::{ |
| classify_trait_object_field, create_wrapper_types_arc, create_wrapper_types_rc, |
| determine_field_ref_mode, extract_type_name, gen_struct_version_hash_ts, get_field_accessor, |
| get_field_name, get_filtered_source_fields_iter, get_option_inner_primitive_name, |
| get_primitive_writer_method_with_encoding, get_struct_name, get_type_id_by_type_ast, |
| is_debug_enabled, is_direct_primitive_type, is_option_encoding_primitive, FieldRefMode, |
| StructField, |
| }; |
| use crate::util::SourceField; |
| use fory_core::types::TypeId; |
| use proc_macro2::{Ident, TokenStream}; |
| use quote::quote; |
| use syn::Field; |
| |
| pub fn gen_reserved_space(fields: &[&Field]) -> TokenStream { |
| let reserved_size_expr: Vec<_> = fields.iter().map(|field| { |
| let ty = &field.ty; |
| match classify_trait_object_field(ty) { |
| StructField::BoxDyn => { |
| quote! { |
| fory_core::types::SIZE_OF_REF_AND_TYPE |
| } |
| } |
| StructField::RcDyn(trait_name) => { |
| let types = create_wrapper_types_rc(&trait_name); |
| let wrapper_ty = types.wrapper_ty; |
| quote! { |
| <#wrapper_ty as fory_core::Serializer>::fory_reserved_space() + fory_core::types::SIZE_OF_REF_AND_TYPE |
| } |
| } |
| StructField::ArcDyn(trait_name) => { |
| let types = create_wrapper_types_arc(&trait_name); |
| let wrapper_ty = types.wrapper_ty; |
| quote! { |
| <#wrapper_ty as fory_core::Serializer>::fory_reserved_space() + fory_core::types::SIZE_OF_REF_AND_TYPE |
| } |
| } |
| StructField::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>::fory_reserved_space() + fory_core::types::SIZE_OF_REF_AND_TYPE |
| } |
| } |
| StructField::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>::fory_reserved_space() + fory_core::types::SIZE_OF_REF_AND_TYPE |
| } |
| } |
| StructField::VecBox(_) => { |
| // Vec<Box<dyn Any>> uses standard Vec serialization |
| quote! { |
| <#ty as fory_core::Serializer>::fory_reserved_space() + fory_core::types::SIZE_OF_REF_AND_TYPE |
| } |
| } |
| StructField::HashMapBox(_, _) => { |
| // HashMap<K, Box<dyn Any>> uses standard HashMap serialization |
| quote! { |
| <#ty as fory_core::Serializer>::fory_reserved_space() + fory_core::types::SIZE_OF_REF_AND_TYPE |
| } |
| } |
| StructField::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>::fory_reserved_space() + fory_core::types::SIZE_OF_REF_AND_TYPE |
| } |
| } |
| StructField::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>::fory_reserved_space() + fory_core::types::SIZE_OF_REF_AND_TYPE |
| } |
| } |
| StructField::Forward => { |
| quote! { |
| <#ty as fory_core::Serializer>::fory_reserved_space() + fory_core::types::SIZE_OF_REF_AND_TYPE |
| } |
| } |
| _ => { |
| quote! { |
| <#ty as fory_core::Serializer>::fory_reserved_space() + fory_core::types::SIZE_OF_REF_AND_TYPE |
| } |
| } |
| } |
| }).collect(); |
| if reserved_size_expr.is_empty() { |
| quote! { 0 } |
| } else { |
| quote! { #(#reserved_size_expr)+* } |
| } |
| } |
| |
| pub fn gen_write_type_info() -> TokenStream { |
| quote! { |
| fory_core::serializer::struct_::write_type_info::<Self>(context) |
| } |
| } |
| |
| /// Generate write code for a field using index-based access (supports tuple structs) |
| pub fn gen_write_field_with_index(field: &Field, index: usize, use_self: bool) -> TokenStream { |
| let value_ts = get_field_accessor(field, index, use_self); |
| let field_name = get_field_name(field, index); |
| gen_write_field_impl(field, &value_ts, &field_name, use_self) |
| } |
| |
| pub fn gen_write_field(field: &Field, ident: &Ident, use_self: bool) -> TokenStream { |
| let value_ts = if use_self { |
| quote! { self.#ident } |
| } else { |
| quote! { #ident } |
| }; |
| let field_name = ident.to_string(); |
| gen_write_field_impl(field, &value_ts, &field_name, use_self) |
| } |
| |
| fn gen_write_field_impl( |
| field: &Field, |
| value_ts: &TokenStream, |
| field_name: &str, |
| use_self: bool, |
| ) -> TokenStream { |
| let ty = &field.ty; |
| // Determine RefMode from field meta (respects #[fory(ref=false)] attribute) |
| let ref_mode = determine_field_ref_mode(field); |
| let base = match classify_trait_object_field(ty) { |
| StructField::BoxDyn => { |
| // Box<dyn Trait> - respect field meta for ref mode |
| quote! { |
| <#ty as fory_core::Serializer>::fory_write(&#value_ts, context, #ref_mode, true, false)?; |
| } |
| } |
| StructField::RcDyn(trait_name) => { |
| // Rc<dyn Trait> - respect field meta for ref mode |
| let types = create_wrapper_types_rc(&trait_name); |
| let wrapper_ty = types.wrapper_ty; |
| let trait_ident = types.trait_ident; |
| quote! { |
| let wrapper = #wrapper_ty::from(#value_ts.clone() as std::rc::Rc<dyn #trait_ident>); |
| <#wrapper_ty as fory_core::Serializer>::fory_write(&wrapper, context, #ref_mode, true, false)?; |
| } |
| } |
| StructField::ArcDyn(trait_name) => { |
| // Arc<dyn Trait> - respect field meta for ref mode |
| let types = create_wrapper_types_arc(&trait_name); |
| let wrapper_ty = types.wrapper_ty; |
| let trait_ident = types.trait_ident; |
| quote! { |
| let wrapper = #wrapper_ty::from(#value_ts.clone() as std::sync::Arc<dyn #trait_ident>); |
| <#wrapper_ty as fory_core::Serializer>::fory_write(&wrapper, context, #ref_mode, true, false)?; |
| } |
| } |
| StructField::VecRc(trait_name) => { |
| // Vec<Rc<dyn Trait>> - respect field meta for ref mode |
| let types = create_wrapper_types_rc(&trait_name); |
| let wrapper_ty = types.wrapper_ty; |
| let trait_ident = types.trait_ident; |
| quote! { |
| let wrapper_vec: Vec<#wrapper_ty> = #value_ts.iter() |
| .map(|item| #wrapper_ty::from(item.clone() as std::rc::Rc<dyn #trait_ident>)) |
| .collect(); |
| <Vec<#wrapper_ty> as fory_core::Serializer>::fory_write(&wrapper_vec, context, #ref_mode, false, true)?; |
| } |
| } |
| StructField::VecArc(trait_name) => { |
| // Vec<Arc<dyn Trait>> - respect field meta for ref mode |
| let types = create_wrapper_types_arc(&trait_name); |
| let wrapper_ty = types.wrapper_ty; |
| let trait_ident = types.trait_ident; |
| quote! { |
| let wrapper_vec: Vec<#wrapper_ty> = #value_ts.iter() |
| .map(|item| #wrapper_ty::from(item.clone() as std::sync::Arc<dyn #trait_ident>)) |
| .collect(); |
| <Vec<#wrapper_ty> as fory_core::Serializer>::fory_write(&wrapper_vec, context, #ref_mode, false, true)?; |
| } |
| } |
| StructField::VecBox(_) => { |
| // Vec<Box<dyn Any>> - respect field meta for ref mode |
| if ref_mode == FieldRefMode::None { |
| quote! { |
| <#ty as fory_core::Serializer>::fory_write_data_generic(&#value_ts, context, true)?; |
| } |
| } else { |
| quote! { |
| <#ty as fory_core::Serializer>::fory_write(&#value_ts, context, #ref_mode, false, true)?; |
| } |
| } |
| } |
| StructField::HashMapBox(_, _) => { |
| // HashMap<K, Box<dyn Any>> - respect field meta for ref mode |
| if ref_mode == FieldRefMode::None { |
| quote! { |
| <#ty as fory_core::Serializer>::fory_write_data_generic(&#value_ts, context, true)?; |
| } |
| } else { |
| quote! { |
| <#ty as fory_core::Serializer>::fory_write(&#value_ts, context, #ref_mode, false, true)?; |
| } |
| } |
| } |
| StructField::HashMapRc(key_ty, trait_name) => { |
| // HashMap<K, Rc<dyn Trait>> - respect field meta for ref mode |
| let types = create_wrapper_types_rc(&trait_name); |
| let wrapper_ty = types.wrapper_ty; |
| let trait_ident = types.trait_ident; |
| quote! { |
| let wrapper_map: std::collections::HashMap<#key_ty, #wrapper_ty> = #value_ts.iter() |
| .map(|(k, v)| (k.clone(), #wrapper_ty::from(v.clone() as std::rc::Rc<dyn #trait_ident>))) |
| .collect(); |
| <std::collections::HashMap<#key_ty, #wrapper_ty> as fory_core::Serializer>::fory_write(&wrapper_map, context, #ref_mode, false, true)?; |
| } |
| } |
| StructField::HashMapArc(key_ty, trait_name) => { |
| // HashMap<K, Arc<dyn Trait>> - respect field meta for ref mode |
| let types = create_wrapper_types_arc(&trait_name); |
| let wrapper_ty = types.wrapper_ty; |
| let trait_ident = types.trait_ident; |
| quote! { |
| let wrapper_map: std::collections::HashMap<#key_ty, #wrapper_ty> = #value_ts.iter() |
| .map(|(k, v)| (k.clone(), #wrapper_ty::from(v.clone() as std::sync::Arc<dyn #trait_ident>))) |
| .collect(); |
| <std::collections::HashMap<#key_ty, #wrapper_ty> as fory_core::Serializer>::fory_write(&wrapper_map, context, #ref_mode, false, true)?; |
| } |
| } |
| StructField::Forward => { |
| // Forward types (trait objects, forward references) - polymorphic, always need type info |
| quote! { |
| <#ty as fory_core::Serializer>::fory_write(&#value_ts, context, #ref_mode, true, false)?; |
| } |
| } |
| _ => { |
| let type_id = get_type_id_by_type_ast(ty); |
| let meta = parse_field_meta(field).unwrap_or_default(); |
| |
| // Check if this is Option<u32> or Option<u64> with encoding attributes |
| // These need special inline handling because the generic Option<T> serializer |
| // doesn't know about field-level encoding attributes. |
| if is_option_encoding_primitive(ty, &meta) { |
| let inner_name = get_option_inner_primitive_name(ty).unwrap(); |
| let writer_method = get_primitive_writer_method_with_encoding(inner_name, &meta); |
| let writer_ident = syn::Ident::new(writer_method, proc_macro2::Span::call_site()); |
| // For Option<primitive>, write null flag first, then value if Some |
| quote! { |
| if let Some(v) = &#value_ts { |
| context.writer.write_i8(fory_core::RefFlag::NotNullValue as i8); |
| context.writer.#writer_ident(*v); |
| } else { |
| context.writer.write_i8(fory_core::RefFlag::Null as i8); |
| } |
| } |
| } |
| // Check if this is a direct primitive type that can use direct writer calls |
| // Only apply when ref_mode is None (no ref tracking needed) |
| else if ref_mode == FieldRefMode::None && is_direct_primitive_type(ty) { |
| let type_name = extract_type_name(ty); |
| if type_name == "String" { |
| // String: call fory_write_data directly |
| quote! { |
| <#ty as fory_core::Serializer>::fory_write_data(&#value_ts, context)?; |
| } |
| } else { |
| // Numeric primitives: use direct buffer methods |
| // For u32/u64, consider encoding attributes |
| let writer_method = |
| get_primitive_writer_method_with_encoding(&type_name, &meta); |
| let writer_ident = |
| syn::Ident::new(writer_method, proc_macro2::Span::call_site()); |
| // For primitives: |
| // - use_self=true: #value_ts is `self.field`, which is T (copy happens automatically) |
| // - use_self=false: #value_ts is `field` from pattern match on &self, which is &T |
| let value_expr = if use_self { |
| quote! { #value_ts } |
| } else { |
| quote! { *#value_ts } |
| }; |
| quote! { |
| context.writer.#writer_ident(#value_expr); |
| } |
| } |
| } else if type_id == TypeId::LIST as u32 |
| || type_id == TypeId::SET as u32 |
| || type_id == TypeId::MAP as u32 |
| { |
| // For collections - respect field meta for ref mode |
| if ref_mode == FieldRefMode::None { |
| quote! { |
| <#ty as fory_core::Serializer>::fory_write_data_generic(&#value_ts, context, true)?; |
| } |
| } else { |
| quote! { |
| <#ty as fory_core::Serializer>::fory_write(&#value_ts, context, #ref_mode, false, true)?; |
| } |
| } |
| } else { |
| // Custom types (struct/enum/ext) - always need mode-dependent type info logic |
| // Determine write_type_info based on mode: |
| // - compatible=true: use need_to_write_type_for_field (struct types need type info) |
| // - compatible=false: use fory_is_polymorphic |
| // This applies regardless of ref_mode because Java always writes type info |
| // for struct-type fields in compatible mode, even for non-nullable fields. |
| quote! { |
| let write_type_info = if context.is_compatible() { |
| fory_core::types::need_to_write_type_for_field( |
| <#ty as fory_core::Serializer>::fory_static_type_id() |
| ) |
| } else { |
| <#ty as fory_core::Serializer>::fory_is_polymorphic() |
| }; |
| <#ty as fory_core::Serializer>::fory_write(&#value_ts, context, #ref_mode, write_type_info, false)?; |
| } |
| } |
| } |
| }; |
| |
| if is_debug_enabled() && use_self { |
| let struct_name = get_struct_name().expect("struct context not set"); |
| let struct_name_lit = syn::LitStr::new(&struct_name, proc_macro2::Span::call_site()); |
| let field_name_lit = syn::LitStr::new(field_name, proc_macro2::Span::call_site()); |
| quote! { |
| fory_core::serializer::struct_::struct_before_write_field( |
| #struct_name_lit, |
| #field_name_lit, |
| (&#value_ts) as &dyn std::any::Any, |
| context, |
| ); |
| #base |
| fory_core::serializer::struct_::struct_after_write_field( |
| #struct_name_lit, |
| #field_name_lit, |
| (&#value_ts) as &dyn std::any::Any, |
| context, |
| ); |
| } |
| } else { |
| base |
| } |
| } |
| |
| pub fn gen_write_data(source_fields: &[SourceField<'_>]) -> TokenStream { |
| let fields: Vec<&Field> = source_fields.iter().map(|sf| sf.field).collect(); |
| let write_fields_ts: Vec<_> = get_filtered_source_fields_iter(source_fields) |
| .map(|sf| gen_write_field_with_index(sf.field, sf.original_index, true)) |
| .collect(); |
| |
| let version_hash_ts = gen_struct_version_hash_ts(&fields); |
| quote! { |
| if context.is_check_struct_version() { |
| let version_hash: i32 = #version_hash_ts; |
| context.writer.write_i32(version_hash); |
| } |
| #(#write_fields_ts)* |
| Ok(()) |
| } |
| } |
| |
| pub fn gen_write() -> TokenStream { |
| quote! { |
| fory_core::serializer::struct_::write::<Self>(self, context, ref_mode, write_type_info) |
| } |
| } |