serialize_fields/lib.rs
1//! # SerializeFields
2//!
3//! A Rust procedural macro that enables **dynamic field selection** for struct serialization.
4//! Control exactly which fields get serialized at runtime using a hierarchical field selector system.
5//!
6//! ## Features
7//!
8//! - 🎯 **Dynamic Field Selection**: Choose which fields to serialize at runtime
9//! - 🌳 **Hierarchical Selection**: Use dot notation for nested structs (`"user.profile.name"`)
10//! - 🔧 **Type Safe**: Compile-time validation of field paths
11//! - 🚀 **Zero Runtime Cost**: Only enabled fields are processed during serialization
12//! - 📦 **Serde Integration**: Works seamlessly with the serde ecosystem
13//! - 🔄 **Collection Support**: Handles `Vec`, `Option`, `HashMap`, and other containers
14//! - 🏗️ **Generic Architecture**: Single trait-based serialization implementation
15//!
16//! ## Quick Start
17//!
18//! ```rust
19//! use serialize_fields::{SerializeFields, SerializeFieldsTrait};
20//! use serde::{Serialize, Deserialize};
21//!
22//! #[derive(SerializeFields, Serialize, Deserialize)]
23//! struct User {
24//! id: u32,
25//! name: Option<String>,
26//! email: Option<String>,
27//! }
28//!
29//! let user = User {
30//! id: 123,
31//! name: Some("Alice".to_string()),
32//! email: Some("alice@example.com".to_string()),
33//! };
34//!
35//! // Create field selector using the trait method
36//! let mut fields = user.serialize_fields();
37//! fields.enable_dot_hierarchy("id");
38//! fields.enable_dot_hierarchy("name");
39//!
40//! // Serialize with selected fields only
41//! let json = serde_json::to_string(&SerializeFields(&user, &fields)).unwrap();
42//! // Output: {"id":123,"name":"Alice"}
43//! ```
44//!
45//! ## Advanced Usage
46//!
47//! ### Nested Structs
48//!
49//! ```rust
50//! # use serialize_fields::{SerializeFields, SerializeFieldsTrait};
51//! # use serde::{Serialize, Deserialize};
52//! #[derive(SerializeFields, Serialize, Deserialize)]
53//! struct User {
54//! id: u32,
55//! profile: UserProfile,
56//! }
57//!
58//! #[derive(SerializeFields, Serialize, Deserialize)]
59//! struct UserProfile {
60//! bio: Option<String>,
61//! avatar_url: Option<String>,
62//! }
63//!
64//! # let user = User {
65//! # id: 123,
66//! # profile: UserProfile {
67//! # bio: Some("Software Engineer".to_string()),
68//! # avatar_url: Some("https://example.com/avatar.jpg".to_string()),
69//! # },
70//! # };
71//! let mut fields = user.serialize_fields();
72//! fields.enable_dot_hierarchy("id");
73//! fields.enable_dot_hierarchy("profile.bio"); // Nested field selection
74//!
75//! let json = serde_json::to_string(&SerializeFields(&user, &fields)).unwrap();
76//! // Output: {"id":123,"profile":{"bio":"Software Engineer"}}
77//! ```
78//!
79//! ### Dynamic Field Selection
80//!
81//! ```rust
82//! # use serialize_fields::{SerializeFields, SerializeFieldsTrait};
83//! # use serde::{Serialize, Deserialize};
84//! # #[derive(SerializeFields, Serialize, Deserialize)]
85//! # struct User { id: u32, name: Option<String>, email: Option<String> }
86//! # let user = User { id: 123, name: Some("Alice".to_string()), email: Some("alice@example.com".to_string()) };
87//! fn serialize_user_with_fields(user: &User, requested_fields: &[&str]) -> String {
88//! let mut selector = user.serialize_fields();
89//!
90//! for field in requested_fields {
91//! selector.enable_dot_hierarchy(field);
92//! }
93//!
94//! serde_json::to_string(&SerializeFields(user, &selector)).unwrap()
95//! }
96//!
97//! // Usage: GET /users/123?fields=id,name
98//! let fields = vec!["id", "name"];
99//! let json = serialize_user_with_fields(&user, &fields);
100//! ```
101
102#![doc(html_root_url = "https://docs.rs/serialize_fields/0.2.3")]
103#![cfg_attr(docsrs, feature(doc_cfg))]
104
105// Re-export the derive macro
106pub use serialize_fields_macro::SerializeFields;
107
108mod macros;
109
110/// Trait for types that can provide field selectors for dynamic serialization.
111///
112/// This trait is automatically implemented by the `#[derive(SerializeFields)]` macro
113/// and provides both field selector creation and serialization functionality.
114///
115/// # Examples
116///
117/// ```rust
118/// # use serialize_fields::{SerializeFields as SerializeFieldsDerive, SerializeFieldsTrait};
119/// # use serde::{Serialize, Deserialize};
120/// #[derive(SerializeFieldsDerive, Serialize, Deserialize)]
121/// struct User {
122/// id: u32,
123/// name: String,
124/// }
125///
126/// # let user = User { id: 1, name: "Alice".to_string() };
127/// // Create a field selector using the trait method
128/// let mut fields = user.serialize_fields();
129/// fields.enable_dot_hierarchy("id");
130/// fields.enable_dot_hierarchy("name");
131/// ```
132pub trait SerializeFieldsTrait {
133 /// The type of field selector for this struct.
134 type FieldSelector: FieldSelector;
135
136 /// Create a new field selector for this type.
137 ///
138 /// This is a convenience method that's equivalent to calling
139 /// `{StructName}SerializeFieldSelector::new()` but provides a more
140 /// ergonomic API through the trait.
141 fn serialize_fields(&self) -> Self::FieldSelector;
142
143 /// Serialize this struct using the provided field selector.
144 ///
145 /// This method is called by the generic `Serialize` implementation
146 /// for `SerializeFields` and handles the actual serialization logic
147 /// with field filtering.
148 ///
149 /// # Arguments
150 ///
151 /// * `field_selector` - The field selector that determines which fields to include
152 /// * `serializer` - The serde serializer to use
153 fn serialize<__S>(
154 &self,
155 field_selector: &Self::FieldSelector,
156 __serializer: __S,
157 ) -> Result<__S::Ok, __S::Error>
158 where
159 __S: serde::Serializer;
160}
161
162/// A wrapper struct that combines data with a field selector for serialization.
163///
164/// This is the core type that enables dynamic field selection. It wraps your data
165/// and a field selector, implementing `Serialize` to only include enabled fields.
166///
167/// # Type Parameters
168///
169/// - `T`: The type of data being serialized
170/// - `S`: The type of field selector (typically generated by the derive macro)
171///
172/// # Examples
173///
174/// ```rust
175/// # use serialize_fields::{SerializeFields, SerializeFields as SerializeFieldsDerive};
176/// # use serde::{Serialize, Deserialize};
177/// # #[derive(SerializeFieldsDerive, Serialize, Deserialize)]
178/// # struct User { id: u32, name: String }
179/// # let user = User { id: 1, name: "Alice".to_string() };
180/// # let mut selector = UserSerializeFieldSelector::new();
181/// # selector.enable_dot_hierarchy("id");
182/// let wrapper = SerializeFields(&user, &selector);
183/// let json = serde_json::to_string(&wrapper).unwrap();
184/// ```
185pub struct SerializeFields<'a, T, S>(pub &'a T, pub &'a S);
186
187impl<'a, T, S> serde::Serialize for SerializeFields<'a, T, S>
188where
189 T: SerializeFieldsTrait<FieldSelector = S>,
190 S: FieldSelector,
191{
192 fn serialize<Se>(&self, serializer: Se) -> Result<Se::Ok, Se::Error>
193 where
194 Se: serde::Serializer,
195 {
196 self.0.serialize(self.1, serializer)
197 }
198}
199
200// Generic implementation for Vec<T> where T implements SerializeFieldsTrait
201impl<'a, T, S> serde::Serialize for SerializeFields<'a, Vec<T>, S>
202where
203 T: SerializeFieldsTrait<FieldSelector = S>,
204 S: FieldSelector,
205{
206 fn serialize<Se>(&self, serializer: Se) -> Result<Se::Ok, Se::Error>
207 where
208 Se: serde::Serializer,
209 {
210 use serde::ser::SerializeSeq;
211
212 let data = self.0;
213 let field_selector = self.1;
214
215 let mut seq = serializer.serialize_seq(Some(data.len()))?;
216
217 for item in data {
218 seq.serialize_element(&SerializeFields(item, field_selector))?;
219 }
220
221 seq.end()
222 }
223}
224
225// Generic implementation for Option<T> where T implements SerializeFieldsTrait
226impl<'a, T, S> serde::Serialize for SerializeFields<'a, Option<T>, S>
227where
228 T: SerializeFieldsTrait<FieldSelector = S>,
229 S: FieldSelector,
230{
231 fn serialize<Se>(&self, serializer: Se) -> Result<Se::Ok, Se::Error>
232 where
233 Se: serde::Serializer,
234 {
235 let data = self.0;
236 let field_selector = self.1;
237
238 match data {
239 Some(inner) => SerializeFields(inner, field_selector).serialize(serializer),
240 None => serializer.serialize_none(),
241 }
242 }
243}
244
245// implement JsonSchema for SerializeFields<T, S> where T implements JsonSchema
246#[cfg(feature = "schemars")]
247impl<'a, T, S> schemars::JsonSchema for SerializeFields<'a, T, S>
248where
249 T: schemars::JsonSchema,
250 S: FieldSelector,
251{
252 // Required methods
253 fn schema_name() -> std::borrow::Cow<'static, str> {
254 T::schema_name()
255 }
256 fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
257 T::json_schema(generator)
258 }
259
260 // Provided methods
261 fn always_inline_schema() -> bool {
262 T::always_inline_schema()
263 }
264 fn inline_schema() -> bool {
265 T::inline_schema()
266 }
267 fn schema_id() -> std::borrow::Cow<'static, str> {
268 T::schema_id()
269 }
270}
271
272/// Helper trait for field selectors to provide common functionality.
273///
274/// This trait is automatically implemented for all generated field selectors.
275pub trait FieldSelector {
276 /// Create a new selector with all fields disabled.
277 fn new() -> Self;
278
279 /// Enable a field using dot notation.
280 ///
281 /// # Examples
282 ///
283 /// ```ignore
284 /// selector.enable_dot_hierarchy("name"); // Simple field
285 /// selector.enable_dot_hierarchy("profile.bio"); // Nested field
286 /// selector.enable_dot_hierarchy("posts.title"); // Field in collection
287 /// ```
288 fn enable_dot_hierarchy(&mut self, field: &str);
289
290 /// Enable a field using a slice of field names.
291 ///
292 /// This is useful when you already have the field path split.
293 ///
294 /// # Examples
295 ///
296 /// ```ignore
297 /// selector.enable(&["name"]); // Simple field
298 /// selector.enable(&["profile", "bio"]); // Nested field
299 /// ```
300 fn enable(&mut self, field_hierarchy: &[&str]);
301}
302
303/// Utility functions for working with field selectors.
304pub mod utils {
305 /// Parse a comma-separated list of field names.
306 ///
307 /// This is useful for parsing query parameters or configuration strings.
308 ///
309 /// # Examples
310 ///
311 /// ```rust
312 /// use serialize_fields::utils::parse_field_list;
313 ///
314 /// let fields = parse_field_list("id,name,profile.bio");
315 /// assert_eq!(fields, vec!["id", "name", "profile.bio"]);
316 /// ```
317 pub fn parse_field_list(fields: &str) -> Vec<&str> {
318 fields
319 .split(',')
320 .map(|s| s.trim())
321 .filter(|s| !s.is_empty())
322 .collect()
323 }
324
325 /// Create a field selector from a list of field names.
326 ///
327 /// This is a convenience function that combines parsing and enabling fields.
328 ///
329 /// # Examples
330 ///
331 /// ```ignore
332 /// use serialize_fields::utils::create_selector_from_list;
333 ///
334 /// let selector: UserSerializeFieldSelector =
335 /// create_selector_from_list("id,name,profile.bio");
336 /// ```
337 pub fn create_selector_from_list<T>(fields: &str) -> T
338 where
339 T: crate::FieldSelector,
340 {
341 let mut selector = T::new();
342 for field in parse_field_list(fields) {
343 selector.enable_dot_hierarchy(field);
344 }
345 selector
346 }
347}