better_debug/lib.rs
1//! Drop-in replacement for `#[derive(Debug)]`
2//!
3//! Have you used `#[derive(Debug)]` on a struct?
4//! Well, you've probably noticed that it prints out all the fields of the struct,
5//! even the ones that you don't want to print out. If you have a `Vec<T>` in your
6//! struct, forget about printing it because that will clutter the logs very badly.
7//!
8//! Or maybe you just want to customize the name of a field, or avoid printing it
9//! completely?
10//!
11//! Look no more! `BetterDebug` is a drop-in replacement for `#[derive(Debug)]` that
12//! allows you to customize the behavior of the `Debug` macro.
13//!
14//! This crate provides an extremely simple, fast, efficient and no-overhead
15//! replacement for the standard library's Debug macro. BetterDebug allows
16//! you to skip fields from being printed, mark them as a secret, use a custom
17//! formatter, etc.
18//!
19//! Please see the documentation below for usage instructions.
20//!
21use proc_macro::TokenStream;
22use proc_macro_error::proc_macro_error;
23use syn::{parse_macro_input, DeriveInput};
24
25mod inner;
26
27/// # Macro usage
28///
29/// Add the following to your Cargo.toml:
30///
31/// ```toml
32/// [dependencies]
33/// better_debug = "1.0"
34/// ```
35///
36/// Then, add the following to your program:
37///
38/// ```rust
39/// use better_debug::BetterDebug;
40///
41/// #[derive(BetterDebug)]
42/// struct Person {
43/// name: String,
44/// age: u8,
45/// secret: String,
46/// }
47/// ```
48///
49/// The above code will implement `fmt::Debug` just like the standard library's
50/// `Debug` macro. Essentially, you've made no changes.
51///
52/// # Cookbook recipes
53///
54/// ## Ignore a custom struct member
55///
56/// This will completely prevent `bar` from being printed.
57///
58/// ```rust
59/// use better_debug::BetterDebug;
60///
61/// #[derive(BetterDebug)]
62/// struct Foo {
63/// #[better_debug(ignore)]
64/// bar: String,
65/// baz: String,
66/// }
67/// ```
68///
69/// ## Rename a field
70///
71/// This will print `bar` as if it were `new_name`.
72/// Note that you can use just about anything, i.e.
73/// `bar`, `Nice Bar!`, etc.
74///
75/// ```rust
76/// use better_debug::BetterDebug;
77///
78/// #[derive(BetterDebug)]
79/// struct Foo {
80/// #[better_debug(rename_to = "new_name")]
81/// bar: String,
82/// baz: String,
83///}
84/// ```
85///
86/// ## Mark a field as a secret
87///
88/// This will set this field's contents to
89/// `<SECRET>`, regardless of its actual contents.
90///
91/// ```rust
92/// use better_debug::BetterDebug;
93///
94/// #[derive(BetterDebug)]
95/// struct Foo {
96/// #[better_debug(secret)]
97/// bar: String,
98/// baz: String,
99///}
100/// ```
101/// ## Use a custom formatter.
102///
103/// You can use a custom function to format the contents of a field.
104///
105/// This function must take a reference to the entire struct and return
106/// `Some(dyn fmt::Debug)` or None to use the default formatter.
107/// You can also return None to prevent the field from being printed.
108///
109/// Note that there's no hard requirement in the return type of the function:
110/// you can return any `Some(T)` as long as `T` is printable, i.e. it implements
111/// `fmt::Debug`. The examples below use `&'static str` for convenience.
112///
113/// ### Use a custom formatter with fallback
114///
115/// ```rust
116/// use better_debug::BetterDebug;
117///
118/// fn foo(foo: &Foo) -> Option<&'static str> {
119/// if foo.bar.len() < 5 {
120/// return Some("lorem ipsum");
121/// }
122/// None
123/// }
124///
125/// #[derive(BetterDebug)]
126/// struct Foo {
127/// #[better_debug(cust_formatter = "foo")]
128/// bar: String,
129/// baz: String,
130///}
131/// ```
132///
133/// ### Use a custom formatter without fallback
134///
135/// ```rust
136/// use better_debug::BetterDebug;
137///
138/// fn foo(foo: &Foo) -> Option<&'static str> {
139/// if foo.bar != "lorem ipsum" {
140/// // If bar isn't equal to "lorem ipsum", then
141/// // don't print anything at all.
142/// return None;
143/// }
144/// Some("lorem ipsum is great")
145/// }
146///
147/// #[derive(BetterDebug)]
148/// struct Foo {
149/// #[better_debug(cust_formatter = "foo", cust_formatter_skip_if_none)]
150/// bar: String,
151/// baz: String,
152///}
153/// ```
154#[proc_macro_derive(BetterDebug, attributes(better_debug))]
155#[proc_macro_error]
156pub fn derive(input: TokenStream) -> proc_macro::TokenStream {
157 let ast = parse_macro_input!(input as DeriveInput);
158 inner::expand(ast)
159 .unwrap_or_else(syn::Error::into_compile_error)
160 .into()
161}