pretty_simple_display/lib.rs
1//! # pretty-simple-display
2//!
3//! Custom derive macros for JSON serialization with pretty and simple formatting options.
4//!
5//! This crate provides four derive macros that implement `Debug` and `Display` traits using JSON serialization:
6//!
7//! - **`DebugPretty`**: Implements `Debug` with pretty-printed JSON output
8//! - **`DisplayPretty`**: Implements `Display` with pretty-printed JSON output
9//! - **`DebugSimple`**: Implements `Debug` with compact JSON output
10//! - **`DisplaySimple`**: Implements `Display` with compact JSON output
11//!
12//! ## Quick Start
13//!
14//! Add this to your `Cargo.toml`:
15//!
16//! ```toml
17//! [dependencies]
18//! pretty-simple-display = "0.1.0"
19//! serde = { version = "1.0", features = ["derive"] }
20//! serde_json = "1.0"
21//! ```
22//!
23//! ## Basic Usage
24//!
25//! ```rust
26//! use pretty_simple_display::{DebugPretty, DisplaySimple};
27//! use serde::Serialize;
28//!
29//! #[derive(Serialize, DebugPretty, DisplaySimple)]
30//! struct User {
31//! id: u64,
32//! name: String,
33//! email: String,
34//! }
35//!
36//! let user = User {
37//! id: 1,
38//! name: "Alice".to_string(),
39//! email: "alice@example.com".to_string(),
40//! };
41//!
42//! // Pretty-printed JSON via Debug
43//! format!("{:?}", user);
44//! // Compact JSON via Display
45//! format!("{}", user);
46//! ```
47//!
48//! ## Output Comparison
49//!
50//! ### Pretty vs Simple Formatting
51//!
52//! **Pretty formatting** (using `DebugPretty` or `DisplayPretty`):
53//! ```json
54//! {
55//! "id": 1,
56//! "name": "Alice",
57//! "email": "alice@example.com"
58//! }
59//! ```
60//!
61//! **Simple formatting** (using `DebugSimple` or `DisplaySimple`):
62//! ```json
63//! {"id":1,"name":"Alice","email":"alice@example.com"}
64//! ```
65//!
66//! ## Advanced Usage
67//!
68//! ### Multiple Derives on Same Struct
69//!
70//! ```rust
71//! use pretty_simple_display::{DebugPretty, DisplaySimple};
72//! use serde::Serialize;
73//!
74//! #[derive(Serialize, DebugPretty, DisplaySimple)]
75//! struct Product {
76//! id: u32,
77//! name: String,
78//! price: f64,
79//! }
80//!
81//! let product = Product {
82//! id: 123,
83//! name: "Widget".to_string(),
84//! price: 29.99,
85//! };
86//!
87//! // Debug uses pretty formatting
88//! println!("{:?}", product);
89//! // Output:
90//! // {
91//! // "id": 123,
92//! // "name": "Widget",
93//! // "price": 29.99
94//! // }
95//!
96//! // Display uses simple formatting
97//! println!("{}", product);
98//! // Output: {"id":123,"name":"Widget","price":29.99}
99//! ```
100//!
101//! ### Working with Enums
102//!
103//! ```rust
104//! use pretty_simple_display::DebugPretty;
105//! use serde::Serialize;
106//!
107//! #[derive(Serialize, DebugPretty)]
108//! enum Status {
109//! Active,
110//! Inactive,
111//! Pending { reason: String },
112//! }
113//!
114//! let status1 = Status::Active;
115//! let status2 = Status::Pending { reason: "Verification".to_string() };
116//!
117//! println!("{:?}", status1); // "Active"
118//! println!("{:?}", status2);
119//! // {
120//! // "Pending": {
121//! // "reason": "Verification"
122//! // }
123//! // }
124//! ```
125//!
126//! ### Nested Structures
127//!
128//! ```rust
129//! use pretty_simple_display::DisplaySimple;
130//! use serde::Serialize;
131//!
132//! #[derive(Serialize)]
133//! struct Address {
134//! street: String,
135//! city: String,
136//! }
137//!
138//! #[derive(Serialize, DisplaySimple)]
139//! struct Person {
140//! name: String,
141//! address: Address,
142//! tags: Vec<String>,
143//! }
144//!
145//! let person = Person {
146//! name: "John".to_string(),
147//! address: Address {
148//! street: "123 Main St".to_string(),
149//! city: "Anytown".to_string(),
150//! },
151//! tags: vec!["developer".to_string(), "rust".to_string()],
152//! };
153//!
154//! println!("{}", person);
155//! // {"name":"John","address":{"street":"123 Main St","city":"Anytown"},"tags":["developer","rust"]}
156//! ```
157//!
158//! ## Error Handling
159//!
160//! All macros handle serialization errors gracefully by displaying an error message instead of panicking:
161//!
162//! ```text
163//! Error serializing to JSON: <error_details>
164//! ```
165//!
166//! ## Requirements
167//!
168//! - Your struct must implement `serde::Serialize`
169//! - The `serde` crate must be available in your dependencies
170//! - Compatible with all types that serde can serialize (structs, enums, primitives, collections, etc.)
171//!
172//! ## Feature Comparison
173//!
174//! | Derive Macro | Trait | Format | Use Case |
175//! |--------------|-------|---------|----------|
176//! | `DebugPretty` | `Debug` | Pretty JSON | Development, debugging, logs |
177//! | `DisplayPretty` | `Display` | Pretty JSON | User-facing output, formatted display |
178//! | `DebugSimple` | `Debug` | Compact JSON | Performance-critical debugging |
179//! | `DisplaySimple` | `Display` | Compact JSON | APIs, compact serialization |
180
181use proc_macro::TokenStream;
182use quote::quote;
183use syn::{DeriveInput, parse_macro_input};
184
185/// Derive macro that implements Debug trait using pretty JSON serialization
186///
187/// This generates a Debug implementation that outputs the struct as
188/// pretty-printed JSON using serde_json::to_string_pretty().
189///
190/// # Example
191/// ```rust
192/// use pretty_simple_display::DebugPretty;
193/// use serde::Serialize;
194///
195/// #[derive(Serialize, DebugPretty)]
196/// struct User {
197/// id: u64,
198/// name: String,
199/// }
200///
201/// let user = User { id: 1, name: "Alice".to_string() };
202/// println!("{:?}", user); // Pretty-printed JSON
203/// ```
204#[proc_macro_derive(DebugPretty)]
205pub fn derive_debug_pretty(input: TokenStream) -> TokenStream {
206 let input = parse_macro_input!(input as DeriveInput);
207 let name = &input.ident;
208
209 let expanded = quote! {
210 impl std::fmt::Debug for #name {
211 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
212 match serde_json::to_string_pretty(self) {
213 Ok(pretty_json) => write!(f, "{}", pretty_json),
214 Err(e) => write!(f, "Error serializing to JSON: {}", e),
215 }
216 }
217 }
218 };
219
220 TokenStream::from(expanded)
221}
222
223/// Derive macro that implements Display trait using pretty JSON serialization
224///
225/// This generates a Display implementation that outputs the struct as
226/// pretty-printed JSON using serde_json::to_string_pretty().
227///
228/// # Example
229/// ```rust
230/// use pretty_simple_display::DisplayPretty;
231/// use serde::Serialize;
232///
233/// #[derive(Serialize, DisplayPretty)]
234/// struct User {
235/// id: u64,
236/// name: String,
237/// }
238///
239/// let user = User { id: 1, name: "Alice".to_string() };
240/// println!("{}", user); // Pretty-printed JSON
241/// ```
242#[proc_macro_derive(DisplayPretty)]
243pub fn derive_display_pretty(input: TokenStream) -> TokenStream {
244 let input = parse_macro_input!(input as DeriveInput);
245 let name = &input.ident;
246
247 let expanded = quote! {
248 impl std::fmt::Display for #name {
249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250 match serde_json::to_string_pretty(self) {
251 Ok(pretty_json) => write!(f, "{}", pretty_json),
252 Err(e) => write!(f, "Error serializing to JSON: {}", e),
253 }
254 }
255 }
256 };
257
258 TokenStream::from(expanded)
259}
260
261/// Derive macro that implements Display trait using compact JSON serialization
262///
263/// This generates a Display implementation that outputs the struct as
264/// compact JSON using serde_json::to_string().
265///
266/// # Example
267/// ```rust
268/// use pretty_simple_display::DisplaySimple;
269/// use serde::Serialize;
270///
271/// #[derive(Serialize, DisplaySimple)]
272/// struct User {
273/// id: u64,
274/// name: String,
275/// }
276///
277/// let user = User { id: 1, name: "Alice".to_string() };
278/// println!("{}", user); // Compact JSON
279/// ```
280#[proc_macro_derive(DisplaySimple)]
281pub fn derive_display_simple(input: TokenStream) -> TokenStream {
282 let input = parse_macro_input!(input as DeriveInput);
283 let name = &input.ident;
284
285 let expanded = quote! {
286 impl std::fmt::Display for #name {
287 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
288 match serde_json::to_string(self) {
289 Ok(json) => write!(f, "{}", json),
290 Err(e) => write!(f, "Error serializing to JSON: {}", e),
291 }
292 }
293 }
294 };
295
296 TokenStream::from(expanded)
297}
298
299/// Derive macro that implements Debug trait using compact JSON serialization
300///
301/// This generates a Debug implementation that outputs the struct as
302/// compact JSON using serde_json::to_string().
303///
304/// # Example
305/// ```rust
306/// use pretty_simple_display::DebugSimple;
307/// use serde::Serialize;
308///
309/// #[derive(Serialize, DebugSimple)]
310/// struct User {
311/// id: u64,
312/// name: String,
313/// }
314///
315/// let user = User { id: 1, name: "Alice".to_string() };
316/// println!("{:?}", user); // Compact JSON
317/// ```
318#[proc_macro_derive(DebugSimple)]
319pub fn derive_debug_simple(input: TokenStream) -> TokenStream {
320 let input = parse_macro_input!(input as DeriveInput);
321 let name = &input.ident;
322
323 let expanded = quote! {
324 impl std::fmt::Debug for #name {
325 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
326 match serde_json::to_string(self) {
327 Ok(json) => write!(f, "{}", json),
328 Err(e) => write!(f, "Error serializing to JSON: {}", e),
329 }
330 }
331 }
332 };
333
334 TokenStream::from(expanded)
335}
336
337// Unit tests are included in the doctests above and integration tests in tests/ directory