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