dbgonly/lib.rs
1/*
2 * The source code for the macro is taken from the dbg! macro, provided from the following source under the MIT license
3 * https://github.com/rust-lang/rust/blob/master/library/std/src/macros.rs#L212-L361
4 */
5
6/// Prints and returns the value of a given expression for quick and dirty
7/// debugging. This version of the macro will print nothing and be optmized
8/// out in release builds.
9///
10/// An example:
11///
12/// ```rust
13/// use dbgonly::dbgonly;
14/// let a = 2;
15/// let b = dbgonly!(a * 2) + 1;
16/// // ^-- prints: [src/main.rs:2] a * 2 = 4
17/// assert_eq!(b, 5);
18/// ```
19///
20/// The macro works by using the `Debug` implementation of the type of
21/// the given expression to print the value to [stderr] along with the
22/// source location of the macro invocation as well as the source code
23/// of the expression.
24///
25/// Invoking the macro on an expression moves and takes ownership of it
26/// before returning the evaluated expression unchanged. If the type
27/// of the expression does not implement `Copy` and you don't want
28/// to give up ownership, you can instead borrow with `dbgonly!(&expr)`
29/// for some expression `expr`.
30///
31/// The `dbgonly!` macro is optimized out in release builds.
32///
33/// Note that the macro is intended as a debugging tool and therefore you
34/// should avoid having uses of it in version control for long periods
35/// (other than in tests and similar).
36/// Debug output from production code is better done with other facilities
37/// such as the [`debug!`] macro from the [`log`] crate.
38///
39/// # Stability
40///
41/// The exact output printed by this macro should not be relied upon
42/// and is subject to future changes.
43///
44/// # Panics
45///
46/// Panics if writing to `io::stderr` fails.
47///
48/// # Further examples
49///
50/// With a method call:
51///
52/// ```rust
53/// use dbgonly::dbgonly;
54/// fn foo(n: usize) {
55/// if let Some(_) = dbgonly!(n.checked_sub(4)) {
56/// // ...
57/// }
58/// }
59///
60/// foo(3)
61/// ```
62///
63/// This prints to [stderr]:
64///
65/// ```text,ignore
66/// [src/main.rs:4] n.checked_sub(4) = None
67/// ```
68///
69/// Naive factorial implementation:
70///
71/// ```rust
72/// use dbgonly::dbgonly;
73/// fn factorial(n: u32) -> u32 {
74/// if dbgonly!(n <= 1) {
75/// dbgonly!(1)
76/// } else {
77/// dbgonly!(n * factorial(n - 1))
78/// }
79/// }
80///
81/// dbgonly!(factorial(4));
82/// ```
83///
84/// This prints to [stderr]:
85///
86/// ```text,ignore
87/// [src/main.rs:3] n <= 1 = false
88/// [src/main.rs:3] n <= 1 = false
89/// [src/main.rs:3] n <= 1 = false
90/// [src/main.rs:3] n <= 1 = true
91/// [src/main.rs:4] 1 = 1
92/// [src/main.rs:5] n * factorial(n - 1) = 2
93/// [src/main.rs:5] n * factorial(n - 1) = 6
94/// [src/main.rs:5] n * factorial(n - 1) = 24
95/// [src/main.rs:11] factorial(4) = 24
96/// ```
97///
98/// The `dbgonly!(..)` macro moves the input:
99///
100/// ```compile_fail
101/// use dbgonly::dbgonly;
102/// /// A wrapper around `usize` which importantly is not Copyable.
103/// #[derive(Debug)]
104/// struct NoCopy(usize);
105///
106/// let a = NoCopy(42);
107/// let _ = dbgonly!(a); // <-- `a` is moved here.
108/// let _ = dbgonly!(a); // <-- `a` is moved again; error!
109/// ```
110///
111/// You can also use `dbgonly!()` without a value to just print the
112/// file and line whenever it's reached.
113///
114/// Finally, if you want to `dbgonly!(..)` multiple values, it will treat them as
115/// a tuple (and return it, too):
116///
117/// ```
118/// use dbgonly::dbgonly;
119/// assert_eq!(dbgonly!(1usize, 2u32), (1, 2));
120/// ```
121///
122/// However, a single argument with a trailing comma will still not be treated
123/// as a tuple, following the convention of ignoring trailing commas in macro
124/// invocations. You can use a 1-tuple directly if you need one:
125///
126/// ```
127/// use dbgonly::dbgonly;
128/// assert_eq!(1, dbgonly!(1u32,)); // trailing comma ignored
129/// assert_eq!((1,), dbgonly!((1u32,))); // 1-tuple
130/// ```
131///
132/// [stderr]: https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)
133/// [`debug!`]: https://docs.rs/log/*/log/macro.debug.html
134/// [`log`]: https://crates.io/crates/log
135#[macro_export]
136#[cfg(debug_assertions)]
137macro_rules! dbgonly {
138 // NOTE: We cannot use `concat!` to make a static string as a format argument
139 // of `eprintln!` because `file!` could contain a `{` or
140 // `$val` expression could be a block (`{ .. }`), in which case the `eprintln!`
141 // will be malformed.
142 () => {
143 eprintln!("[{}:{}]", file!(), line!())
144 };
145 ($val:expr $(,)?) => {
146 // Use of `match` here is intentional because it affects the lifetimes
147 // of temporaries - https://stackoverflow.com/a/48732525/1063961
148 match $val {
149 tmp => {
150 eprintln!("[{}:{}] {} = {:#?}",
151 file!(), line!(), stringify!($val), &tmp);
152 tmp
153 }
154 }
155 };
156 ($($val:expr),+ $(,)?) => {
157 ($(dbgonly!($val)),+,)
158 };
159}
160
161#[macro_export]
162#[cfg(not(debug_assertions))]
163macro_rules! dbgonly {
164 () => {};
165 ($val:expr $(,)?) => {
166 match $val {
167 tmp => tmp
168 }
169 };
170 ($($val:expr),+ $(,)?) => {
171 ($(dbgonly!($val)),+,)
172 };
173}