fory_derive/lib.rs
1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! # Fory Derive Macros
19//!
20//! This crate provides procedural macros for the Fory serialization framework.
21//! It generates serialization and deserialization code for Rust types.
22//! Most applications should import these macros from the `fory` facade crate,
23//! which also provides the runtime API used by generated code. Direct
24//! `fory-derive` usage is for crates that intentionally depend on the
25//! lower-level `fory-core` runtime crate.
26//!
27//! ## Available Macros
28//!
29//! ### `#[derive(ForyStruct)]`, `#[derive(ForyEnum)]`, `#[derive(ForyUnion)]`
30//!
31//! Generates `Serializer` implementations for structs, pure enums, and tagged
32//! unions with payload variants.
33//!
34//! **Supported Types:**
35//! - `ForyStruct`: named, tuple, and unit structs
36//! - `ForyEnum`: pure unit enums
37//! - `ForyUnion`: enums with payload variants
38//!
39//! **Example:**
40//! ```rust
41//! use fory_derive::{ForyEnum, ForyStruct};
42//! use std::collections::HashMap;
43//!
44//! #[derive(ForyStruct, Debug, PartialEq)]
45//! struct Person {
46//! name: String,
47//! age: i32,
48//! address: Address,
49//! hobbies: Vec<String>,
50//! metadata: HashMap<String, String>,
51//! }
52//!
53//! #[derive(ForyStruct, Debug, PartialEq)]
54//! struct Address {
55//! street: String,
56//! city: String,
57//! }
58//!
59//! #[derive(ForyEnum, Debug, PartialEq, Default)]
60//! enum Status {
61//! #[default]
62//! Active,
63//! Inactive,
64//! Suspended,
65//! }
66//! ```
67//!
68//! ### `#[derive(ForyRow)]`
69//!
70//! Generates Standard Row Format serialization and borrowed field views for a
71//! named struct. The macro implements `RowValue` and the root `Row` marker.
72//! Enums, unions, tuple structs, and unit structs are rejected at compile time.
73//!
74//! **Supported Types:**
75//! - Fixed values: `bool`, `i8`, `i16`, `i32`, `i64`, `f32`, `f64`, `Date`,
76//! `Timestamp`, and `Duration`
77//! - Variable values: `String` and `&str`, binary `Vec<u8>` and `&[u8]`, fixed
78//! and variable arrays, `BTreeMap`, and other derived row structs
79//! - `Option<T>` for nullable fields and array elements
80//! - Every field type must implement `RowValue`
81//!
82//! **Example:**
83//! ```rust
84//! use fory_core::error::Error;
85//! use fory_core::row::{from_row, to_row};
86//! use fory_derive::ForyRow;
87//!
88//! #[derive(ForyRow)]
89//! struct UserProfile {
90//! id: i64,
91//! username: String,
92//! email: Option<String>,
93//! }
94//!
95//! # fn main() -> Result<(), Error> {
96//! let bytes = to_row(&UserProfile {
97//! id: 7,
98//! username: "fory".to_owned(),
99//! email: None,
100//! })?;
101//! let view = from_row::<UserProfile>(&bytes)?;
102//! assert_eq!(view.id()?, 7);
103//! assert_eq!(view.username()?, "fory");
104//! assert_eq!(view.email()?, None);
105//! # Ok(())
106//! # }
107//! ```
108//!
109//! ## Generated Code
110//!
111//! ### For `#[derive(ForyStruct)]`, `#[derive(ForyEnum)]`, and `#[derive(ForyUnion)]`
112//!
113//! The macro generates:
114//! - `Serializer` trait implementation
115//! - Serialization methods for writing data to buffers
116//! - Deserialization methods for reading data from buffers
117//! - Type ID management for cross-language compatibility
118//!
119//! ### For `#[derive(ForyRow)]`
120//!
121//! The macro generates:
122//! - A `RowValue` implementation and a root `Row` marker implementation
123//! - A borrowed view type whose visibility matches the source struct
124//! - `RowView` backing-byte access and cheap `Copy`/`Clone` views
125//! - One declaration-order field method preserving each source field's visibility
126//! - Field methods returning `Result<<Field as RowValue>::View<'_>, Error>`
127//!
128//! ## Attributes
129//!
130//! - **`#[fory(debug)]` / `#[fory(debug = true)]`**: Enables per-field debug instrumentation
131//! for the annotated struct, allowing you to install custom hooks via
132//! `fory_core::serializer::struct_`.
133//! - **`#[fory(evolving = false)]`**: Disables compatible struct type IDs for the annotated
134//! struct, forcing STRUCT/NAMED_STRUCT even when compatible mode is enabled.
135//! - **`#[fory(skip)]`**: Marks an individual field (or enum variant) to be ignored by the
136//! generated serializer, retaining compatibility with previous releases.
137//! - **`#[fory(generate_default)]`**: Enables the macro to generate `Default` implementation.
138//! By default, `ForyStruct` does NOT generate `impl Default` to avoid conflicts with existing
139//! `Default` implementations. This attribute is not valid with `target`.
140//! - **`#[fory(target = path::Type)]`**: Makes the derived declaration an external structural
141//! serializer for the target type. Generated code accesses and constructs the target directly;
142//! the serializer declaration itself is never instantiated.
143//! - **`#[fory(with = SerializerType)]`**: Selects a serializer whose target is the exact field
144//! value node. Use carrier serializers for exact wrapper or container nodes, and use `list`,
145//! `map`, or `tuple` metadata to select serializers recursively at child nodes.
146//! - **`#[fory(default)]`**: Marks the fallible deserialization default `ForyUnion` variant.
147//! `ForyUnion` requires exactly one default variant.
148//!
149//! ## Field Types
150//!
151//! The object-format derives support a wide range of field types:
152//!
153//! **Primitive Types:**
154//! - `bool`, `i8`, `i16`, `i32`, `i64`, `f32`, `f64`
155//! - `String`
156//! - `Vec<u8>` for binary data
157//!
158//! **Collections:**
159//! - `Vec<T>` where `T` implements the appropriate trait
160//! - `HashMap<K, V>` and `BTreeMap<K, V>` where keys and values implement the trait
161//! - `Option<T>` for nullable values
162//!
163//! **Date/Time:**
164//! - `fory::Date`
165//! - `fory::Timestamp`
166//! - `fory::Duration`
167//! - `chrono::NaiveDate`, `chrono::NaiveDateTime`, and `chrono::Duration` when the `chrono` feature is enabled
168//!
169//! **Custom Types:**
170//! - Any type that implements `Serializer`
171//!
172//! `ForyRow` uses the separate, exact type set documented under its macro
173//! section. A row field implements `RowValue`; only derived structs, arrays,
174//! and maps implement the root `Row` marker.
175//!
176//! Derived structs, enums, and unions can be used behind
177//! `Arc<dyn Any + Send + Sync>` when the concrete type satisfies `Send + Sync`.
178//! Known non-`Send + Sync` field types such as `Rc<T>` and `RefCell<T>` are not
179//! eligible for that carrier.
180//!
181//! ## Usage with Fory
182//!
183//! After deriving the macros, you can use the types with the Fory serialization
184//! framework:
185//!
186//! ```rust
187//! use fory_core::{fory::Fory, error::Error};
188//! use fory_derive::{ForyEnum, ForyStruct, ForyUnion};
189//!
190//! #[derive(ForyStruct, Debug, PartialEq)]
191//! struct MyData {
192//! value: i32,
193//! text: String,
194//! }
195//!
196//! fn main() -> Result<(), Error> {
197//! let mut fory = Fory::builder().xlang(true).build();
198//! fory.register_by_name::<MyData>("example.MyData")?;
199//!
200//! let data = MyData {
201//! value: 42,
202//! text: "Hello, Fory!".to_string(),
203//! };
204//!
205//! let serialized = fory.serialize(&data)?;
206//! let deserialized: MyData = fory.deserialize(&serialized)?;
207//!
208//! assert_eq!(data, deserialized);
209//! Ok(())
210//! }
211//! ```
212//!
213//! ## Performance Considerations
214//!
215//! - **`Fory`**: Best for complex object graphs with references and nested structures
216//! - **`ForyRow`**: Provides lazy, borrowed access to Standard Row Format data
217//! - Both macros generate optimized code with minimal runtime overhead
218
219use fory_row::derive_row;
220use proc_macro::TokenStream;
221use syn::{
222 parse_macro_input, spanned::Spanned, Attribute, Data, DeriveInput, Fields, LitBool, Type,
223};
224
225mod fory_row;
226mod object;
227mod runtime_root;
228mod util;
229
230/// Derive macro for struct serialization.
231#[proc_macro_derive(ForyStruct, attributes(fory))]
232pub fn proc_macro_derive_fory_struct(input: proc_macro::TokenStream) -> TokenStream {
233 let input = parse_macro_input!(input as DeriveInput);
234 if !matches!(input.data, Data::Struct(_)) {
235 return syn::Error::new(
236 input.ident.span(),
237 "ForyStruct can only be derived for structs; use ForyEnum for pure enums or ForyUnion for data-carrying enums",
238 )
239 .into_compile_error()
240 .into();
241 }
242 derive_serializer(input)
243}
244
245/// Derive macro for pure enum serialization.
246#[proc_macro_derive(ForyEnum, attributes(fory))]
247pub fn proc_macro_derive_fory_enum(input: proc_macro::TokenStream) -> TokenStream {
248 let input = parse_macro_input!(input as DeriveInput);
249 let Data::Enum(data_enum) = &input.data else {
250 return syn::Error::new(input.ident.span(), "ForyEnum can only be derived for enums")
251 .into_compile_error()
252 .into();
253 };
254 if data_enum
255 .variants
256 .iter()
257 .any(|variant| !matches!(variant.fields, Fields::Unit))
258 {
259 return syn::Error::new(
260 input.ident.span(),
261 "ForyEnum is only for pure unit enums; use ForyUnion for enum variants with payloads",
262 )
263 .into_compile_error()
264 .into();
265 }
266 derive_serializer(input)
267}
268
269/// Derives serialization for data-carrying Rust enums.
270///
271/// Xlang-compatible unit and single-payload variants use the Fory `UNION`
272/// representation. Native multi-field tuple and named variants use the native
273/// `ENUM` representation.
274#[proc_macro_derive(ForyUnion, attributes(fory))]
275pub fn proc_macro_derive_fory_union(input: proc_macro::TokenStream) -> TokenStream {
276 let input = parse_macro_input!(input as DeriveInput);
277 if let Err(err) = object::derive_union::validate_input(&input) {
278 return err.into_compile_error().into();
279 }
280 derive_serializer(input)
281}
282
283fn derive_serializer(input: DeriveInput) -> TokenStream {
284 let attrs = match parse_fory_attrs(&input.attrs) {
285 Ok(attrs) => attrs,
286 Err(err) => return err.into_compile_error().into(),
287 };
288 let runtime_root = match runtime_root::resolve_runtime_root() {
289 Ok(root) => root,
290 Err(err) => return err.into_compile_error().into(),
291 };
292
293 object::derive_serializer(&input, attrs, runtime_root)
294}
295
296/// Derive macro for Standard Row Format serialization.
297///
298/// This macro accepts named structs whose fields implement `RowValue`. It
299/// implements `RowValue` and the root `Row` marker, preserves field declaration
300/// order, and generates a borrowed view with field methods that return `Result`.
301///
302/// # Example
303///
304/// ```rust
305/// use fory_core::error::Error;
306/// use fory_core::row::{from_row, to_row};
307/// use fory_derive::ForyRow;
308///
309/// #[derive(ForyRow)]
310/// struct UserProfile {
311/// id: i64,
312/// username: String,
313/// email: Option<String>,
314/// }
315///
316/// # fn main() -> Result<(), Error> {
317/// let bytes = to_row(&UserProfile {
318/// id: 7,
319/// username: "fory".to_owned(),
320/// email: None,
321/// })?;
322/// let view = from_row::<UserProfile>(&bytes)?;
323/// assert_eq!(view.username()?, "fory");
324/// # Ok(())
325/// # }
326/// ```
327#[proc_macro_derive(ForyRow)]
328pub fn proc_macro_derive_fory_row(input: proc_macro::TokenStream) -> TokenStream {
329 let input = parse_macro_input!(input as DeriveInput);
330 let runtime_root = match runtime_root::resolve_runtime_root() {
331 Ok(root) => root,
332 Err(err) => return err.into_compile_error().into(),
333 };
334 derive_row(&input, runtime_root)
335}
336
337/// Parsed fory attributes
338pub(crate) struct ForyAttrs {
339 pub debug_enabled: bool,
340 pub generate_default: bool,
341 pub evolving: Option<bool>,
342 pub target: Option<Type>,
343}
344
345/// Parse fory attributes and return ForyAttrs
346fn parse_fory_attrs(attrs: &[Attribute]) -> syn::Result<ForyAttrs> {
347 let mut debug_flag: Option<bool> = None;
348 let mut generate_default_flag: Option<bool> = None;
349 let mut evolving_flag: Option<bool> = None;
350 let mut target: Option<Type> = None;
351
352 for attr in attrs {
353 if attr.path().is_ident("fory") {
354 attr.parse_nested_meta(|meta| {
355 if meta.path.is_ident("debug") {
356 let value = if meta.input.is_empty() {
357 true
358 } else {
359 let lit: LitBool = meta.value()?.parse()?;
360 lit.value
361 };
362 debug_flag = match debug_flag {
363 Some(existing) if existing != value => {
364 return Err(syn::Error::new(
365 meta.path.span(),
366 "conflicting `debug` attribute values",
367 ));
368 }
369 Some(_) => debug_flag,
370 None => Some(value),
371 };
372 } else if meta.path.is_ident("generate_default") {
373 let value = if meta.input.is_empty() {
374 true
375 } else {
376 let lit: LitBool = meta.value()?.parse()?;
377 lit.value
378 };
379 generate_default_flag = match generate_default_flag {
380 Some(existing) if existing != value => {
381 return Err(syn::Error::new(
382 meta.path.span(),
383 "conflicting `generate_default` attribute values",
384 ));
385 }
386 Some(_) => generate_default_flag,
387 None => Some(value),
388 };
389 } else if meta.path.is_ident("evolving") {
390 let value = if meta.input.is_empty() {
391 true
392 } else {
393 let lit: LitBool = meta.value()?.parse()?;
394 lit.value
395 };
396 evolving_flag = match evolving_flag {
397 Some(existing) if existing != value => {
398 return Err(syn::Error::new(
399 meta.path.span(),
400 "conflicting `evolving` attribute values",
401 ));
402 }
403 Some(_) => evolving_flag,
404 None => Some(value),
405 };
406 } else if meta.path.is_ident("target") {
407 if target.is_some() {
408 return Err(syn::Error::new(
409 meta.path.span(),
410 "duplicate `target` attribute",
411 ));
412 }
413 target = Some(meta.value()?.parse()?);
414 } else {
415 return Err(meta.error("unsupported type-level fory attribute"));
416 }
417 Ok(())
418 })?;
419 }
420 }
421
422 if let Some(target) = &target {
423 if generate_default_flag == Some(true) {
424 return Err(syn::Error::new(
425 target.span(),
426 "`generate_default` is not valid for an external structural serializer",
427 ));
428 }
429 }
430
431 Ok(ForyAttrs {
432 debug_enabled: debug_flag.unwrap_or(false),
433 generate_default: generate_default_flag.unwrap_or(false),
434 evolving: evolving_flag,
435 target,
436 })
437}
438
439#[cfg(test)]
440mod tests {
441 use super::*;
442 use quote::ToTokens;
443 use syn::parse_quote;
444
445 #[test]
446 fn parses_external_target() {
447 let input: DeriveInput = parse_quote! {
448 #[fory(target = external::User)]
449 struct UserSerializer {
450 name: String,
451 }
452 };
453 let attrs = parse_fory_attrs(&input.attrs).unwrap();
454 assert_eq!(
455 attrs.target.unwrap().to_token_stream().to_string(),
456 "external :: User"
457 );
458 }
459
460 #[test]
461 fn rejects_external_std_default() {
462 let input: DeriveInput = parse_quote! {
463 #[fory(target = external::User, generate_default)]
464 struct UserSerializer {
465 name: String,
466 }
467 };
468 let err = match parse_fory_attrs(&input.attrs) {
469 Ok(_) => panic!("external structural serializers must reject `generate_default`"),
470 Err(err) => err,
471 };
472 assert!(err.to_string().contains("generate_default` is not valid"));
473 }
474}