Skip to main content

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