debug2/
lib.rs

1#![warn(missing_docs)]
2
3//! `debug2` is a pretty printing crate based on [`std::fmt`]
4//!
5//! # Why not just use [`Debug`]
6//!
7//! The [`Debug`] trait is good, but the problem is it is not very good at nested stuctures.
8//! Either you use `{:?}` and get a line that is too long, or too many lines with not enough
9//! information on them.
10//!
11//! ```rust
12//! let complex_structure = vec![
13//!     vec![Some(1), Some(2), Some(3), None],
14//!     vec![Some(2), None],
15//!     vec![Some(4), Some(7)],
16//!     vec![Some(1), Some(2), Some(3), None],
17//! ];
18//!
19//! let one_line = format!("{:?}", complex_structure);
20//!
21//! assert_eq!(one_line, "[[Some(1), Some(2), Some(3), None], [Some(2), None], [Some(4), Some(7)], [Some(1), Some(2), Some(3), None]]");
22//!
23//! let many_lines = format!("{:#?}", complex_structure);
24//!
25//! assert_eq!(many_lines, "[
26//!     [
27//!         Some(
28//!             1,
29//!         ),
30//!         Some(
31//!             2,
32//!         ),
33//!         Some(
34//!             3,
35//!         ),
36//!         None,
37//!     ],
38//!     [
39//!         Some(
40//!             2,
41//!         ),
42//!         None,
43//!     ],
44//!     [
45//!         Some(
46//!             4,
47//!         ),
48//!         Some(
49//!             7,
50//!         ),
51//!     ],
52//!     [
53//!         Some(
54//!             1,
55//!         ),
56//!         Some(
57//!             2,
58//!         ),
59//!         Some(
60//!             3,
61//!         ),
62//!         None,
63//!     ],
64//! ]")
65//! ```
66//!
67//! `pprint` aims to be a third alternative, that gets this correct.
68//!
69//! ```rust
70//! use debug2::pprint;
71//!
72//! let complex_structure = vec![
73//!     vec![Some(1), Some(2), Some(3), None],
74//!     vec![Some(2), None],
75//!     vec![Some(4), Some(7)],
76//!     vec![Some(1), Some(2), Some(3), None],
77//!     vec![Some(2), None],
78//!     vec![Some(4), Some(7)],
79//!     vec![Some(1), Some(2), Some(3), None],
80//!     vec![Some(2), None],
81//!     vec![Some(4), Some(7)],
82//! ];
83//!
84//! assert_eq!(
85//!     pprint(complex_structure),
86//!     "\
87//! [
88//!     [Some(1), Some(2), Some(3), None],
89//!     [Some(2), None],
90//!     [Some(4), Some(7)],
91//!     [Some(1), Some(2), Some(3), None],
92//!     [Some(2), None],
93//!     [Some(4), Some(7)],
94//!     [Some(1), Some(2), Some(3), None],
95//!     [Some(2), None],
96//!     [Some(4), Some(7)],
97//! ]"
98//! );
99//! ```
100//!
101//! To use, derive [`Debug`] for your types, and then use [`pprint`] to print them.
102//!
103//! You can also manually implement [`Debug`], using a subset of the API in [`std::fmt::Formatter`]
104//!
105//! # Limitations
106//! - Speed: While doing this will always mean extra work, this crate is paticularly inefficient.
107//! - Prevalence: Almost every type implements [`std::fmt::Debug`], but not this type
108//! - The derive isn't great: The derive macro for [`std::fmt::Debug`] works everywhere. This one
109//!   is kind of basic, and will probably not work everywhere it should.
110
111use std::fmt::{Debug as StdDebug, Error, Result, Write};
112
113mod builders;
114mod std_impls;
115
116pub use builders::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple};
117
118pub use debug2_derive::*;
119
120const MAX_LEN: usize = 80;
121/// Pretty Printed Formatting
122///
123/// This is much like [`std::fmt::Debug`], but it supports much better multiline output
124///
125/// # Examples
126///
127/// ```rust
128/// use debug2::{pprint, Debug};
129///
130/// #[derive(Debug)]
131/// struct Numbers {
132///     a: Vec<Vec<i32>>,
133///     b: String,
134/// }
135///
136/// let a = Numbers {
137///     a: vec![vec![10; 10]; 2],
138///     b: "FooBar".to_owned(),
139/// };
140///
141/// assert_eq!(
142///     pprint(&a),
143///     "\
144/// Numbers {
145///     a: [
146///         [10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
147///         [10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
148///     ],
149///     b: \"FooBar\",
150/// }"
151/// );
152/// ```
153///
154/// You can also implement `fmt` manually, using an API much like [`std::fmt::Formatter`]
155///
156/// ```rust
157/// use debug2::{pprint, Debug, Formatter};
158/// use std::fmt;
159///
160/// struct Chunked10([u8; 100]);
161///
162/// impl Debug for Chunked10 {
163///     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
164///         f.debug_list().entries(self.0.chunks(10)).finish()
165///     }
166/// }
167///
168/// assert_eq!(
169///     pprint(Chunked10([0; 100])),
170///     "\
171/// [
172///     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
173///     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
174///     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
175///     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
176///     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
177///     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
178///     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
179///     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
180///     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
181///     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
182/// ]"
183/// );
184/// ```
185pub trait Debug {
186    /// Formats the value using the given formatter.
187    ///
188    /// Note that this may be called more than once for any invocation of `pprint`, if you do
189    /// side effects in this, make sure they are idempotent. In general, don't relly on how often
190    /// this function is called, as it may change in a future release.
191    fn fmt(&self, f: &mut Formatter<'_>) -> Result;
192}
193
194/// Configuration for formatting.
195///
196/// A `Formatter` represents various options related to formatting. Users do not
197/// construct `Formatter`s directly; a mutable reference to one is passed to
198/// the `fmt` method of [`Debug`].
199///
200/// To interact with a `Formatter`, you'll call various methods to change the
201/// various options related to formatting. For examples, please see the
202/// documentation of the methods defined on `Formatter` below.
203pub struct Formatter<'a> {
204    buf: &'a mut (dyn Write + 'a),
205    mode: Mode,
206}
207
208#[derive(Clone, Copy, PartialEq, Eq)]
209enum Mode {
210    Pretty,
211    Flat,
212}
213
214fn flatprint_checked<T: Debug>(x: T) -> std::result::Result<String, Error> {
215    pprint_mode(x, Mode::Flat)
216}
217
218fn pprint_mode<T: Debug>(x: T, mode: Mode) -> std::result::Result<String, Error> {
219    let mut out = String::new();
220    let mut f = Formatter {
221        buf: &mut out,
222        mode,
223    };
224    x.fmt(&mut f)?;
225    Ok(out)
226}
227
228/// Pretty Print an item to a string, or return an error
229///
230/// ```rust
231/// use debug2::{pprint_checked, Debug, Formatter};
232/// use std::fmt;
233///
234/// struct Good;
235/// struct Bad;
236///
237/// impl Debug for Good {
238///     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
239///         f.debug_struct("Good").finish()
240///     }
241/// }
242///
243/// impl Debug for Bad {
244///     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
245///         Err(fmt::Error)
246///     }
247/// }
248///
249/// assert!(pprint_checked(Good).is_ok());
250/// assert!(pprint_checked(Bad).is_err());
251/// ```
252pub fn pprint_checked<T: Debug>(x: T) -> std::result::Result<String, Error> {
253    let flat = flatprint_checked(&x)?;
254    if flat.len() <= MAX_LEN {
255        Ok(flat)
256    } else {
257        pprint_mode(x, Mode::Pretty)
258    }
259}
260
261/// Pretty Print an item to a string
262///
263/// ```rust
264/// use debug2::pprint;
265///
266/// let x: Vec<Option<&[i32]>> = vec![Some(&[1; 20]), None, None, Some(&[1, 2, 3])];
267///
268/// assert_eq!(
269///     pprint(x),
270///     "\
271/// [
272///     Some([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]),
273///     None,
274///     None,
275///     Some([1, 2, 3]),
276/// ]"
277/// );
278/// ```
279///
280/// Note that while this takes a `T`, you can also pass a reference due to the
281/// `impl<T: Debug> Debug for `&T`
282///
283/// # Panics
284///
285/// This will panic if `<T as Debug>::fmt` returns an error
286///
287pub fn pprint<T: Debug>(x: T) -> String {
288    pprint_checked(x).unwrap()
289}
290
291impl<'a> Formatter<'a> {
292    fn write_debug<T: StdDebug>(&mut self, val: &T) -> Result {
293        write!(self.buf, "{:?}", val)
294    }
295
296    fn write_str(&mut self, data: &str) -> Result {
297        self.buf.write_str(data)
298    }
299
300    /// Creates a [`DebugStruct`] builder designed to assist with creation of
301    /// [`Debug`] implementations for structs.
302    ///
303    ///
304    /// # Examples
305    ///
306    /// ```rust
307    /// use debug2::{pprint, Debug, Formatter};
308    /// use std::fmt;
309    /// use std::net::Ipv4Addr;
310    ///
311    /// struct Foo {
312    ///     bar: i32,
313    ///     baz: String,
314    ///     addr: Ipv4Addr,
315    /// }
316    ///
317    /// impl Debug for Foo {
318    ///     fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
319    ///         fmt.debug_struct("Foo")
320    ///             .field("bar", &self.bar)
321    ///             .field("baz", &self.baz)
322    /// # // TODO: The viersion in `std` uses `format_args`, which gives
323    /// # // a different result, because it doesn't have "" around the content
324    /// # // I should have a macro that delegated to `format!`, but returns a
325    /// # // newtype that doesnt add quotes
326    ///             .field("addr", &format!("{}", self.addr))
327    ///             .finish()
328    ///     }
329    /// }
330    ///
331    /// assert_eq!(
332    ///     "Foo { bar: 10, baz: \"Hello World\", addr: \"127.0.0.1\" }",
333    ///     pprint(Foo {
334    ///         bar: 10,
335    ///         baz: "Hello World".to_string(),
336    ///         addr: Ipv4Addr::new(127, 0, 0, 1),
337    ///     })
338    /// );
339    /// ```
340    pub fn debug_struct<'b>(&'b mut self, name: &str) -> DebugStruct<'b, 'a> {
341        builders::debug_struct_new(self, name)
342    }
343
344    /// Creates a [`DebugTuple`] builder designed to assist with creation of
345    /// [`Debug`] implementations for tuple structs.
346    ///
347    /// # Examples
348    ///
349    /// ```rust
350    /// use debug2::{pprint, Debug, Formatter};
351    /// use std::fmt;
352    /// use std::marker::PhantomData;
353    ///
354    /// struct Foo<T>(i32, String, PhantomData<T>);
355    ///
356    /// impl<T> Debug for Foo<T> {
357    ///     fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
358    ///         fmt.debug_tuple("Foo")
359    ///             .field(&self.0)
360    ///             .field(&self.1)
361    ///             .field(&format!("_"))
362    ///             .finish()
363    ///     }
364    /// }
365    ///
366    /// assert_eq!(
367    ///     "Foo(10, \"Hello\", \"_\")",
368    ///     pprint(Foo(10, "Hello".to_string(), PhantomData::<u8>))
369    /// );
370    /// ```
371    pub fn debug_tuple<'b>(&'b mut self, name: &str) -> DebugTuple<'b, 'a> {
372        builders::debug_tuple_new(self, name)
373    }
374
375    /// Creates a [`DebugList`] builder designed to assist with creation of
376    /// [`Debug`] implementations for list-like structures.
377    ///
378    /// # Examples
379    ///
380    /// ```rust
381    /// use debug2::{pprint, Debug, Formatter};
382    /// use std::fmt;
383    ///
384    /// struct Foo(Vec<i32>);
385    ///
386    /// impl Debug for Foo {
387    ///     fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
388    ///         fmt.debug_list().entries(self.0.iter()).finish()
389    ///     }
390    /// }
391    ///
392    /// assert_eq!(pprint(Foo(vec![10, 11])), "[10, 11]");
393    /// ```
394    pub fn debug_list<'b>(&'b mut self) -> DebugList<'b, 'a> {
395        builders::debug_list_new(self)
396    }
397
398    /// Creates a [`DebugSet`] builder designed to assist with creation of
399    /// [`Debug`] implementations for set-like structures.
400    ///
401    /// # Examples
402    ///
403    /// ```rust
404    /// use debug2::{pprint, Debug, Formatter};
405    /// use std::fmt;
406    ///
407    /// struct Foo(Vec<i32>);
408    ///
409    /// impl Debug for Foo {
410    ///     fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
411    ///         fmt.debug_set().entries(self.0.iter()).finish()
412    ///     }
413    /// }
414    ///
415    /// assert_eq!(pprint(Foo(vec![10, 11])), "{10, 11}");
416    /// ```
417    pub fn debug_set<'b>(&'b mut self) -> DebugSet<'b, 'a> {
418        builders::debug_set_new(self)
419    }
420
421    /// Creates a [`DebugMap`] builder designed to assist with creation of
422    /// [`Debug`] implementations for map-like structures.
423    ///
424    /// # Examples
425    ///
426    /// ```rust
427    /// use debug2::{pprint, Debug, Formatter};
428    /// use std::fmt;
429    ///
430    /// struct Foo(Vec<(String, i32)>);
431    ///
432    /// impl Debug for Foo {
433    ///     fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
434    ///         fmt.debug_map()
435    ///             .entries(self.0.iter().map(|&(ref k, ref v)| (k, v)))
436    ///             .finish()
437    ///     }
438    /// }
439    ///
440    /// assert_eq!(
441    ///     pprint(Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
442    ///     r#"{"A": 10, "B": 11}"#
443    /// );
444    /// ```
445    pub fn debug_map<'b>(&'b mut self) -> DebugMap<'b, 'a> {
446        builders::debug_map_new(self)
447    }
448}
449
450impl<'a> Formatter<'a> {
451    fn wrap_buf<'b, 'c, F>(&'b mut self, wrap: F) -> Formatter<'c>
452    where
453        'b: 'c,
454        F: FnOnce(&'b mut (dyn Write + 'b)) -> &'c mut (dyn Write + 'c),
455    {
456        Formatter {
457            // We want to change this
458            buf: wrap(self.buf),
459
460            // And preserve these
461            mode: self.mode
462
463            // flags: self.flags,
464            // fill: self.fill,
465            // align: self.align,
466            // width: self.width,
467            // precision: self.precision,
468        }
469    }
470
471    fn is_pretty(&self) -> bool {
472        self.mode == Mode::Pretty
473    }
474}
475
476/// Prints and returns the value of a given expression for quick and dirty debugging.
477///
478/// Like [`std::dbg`], but used [`crate::Debug`] instead of [`std::fmt::Debug`]
479///
480/// # Stability
481///
482/// The exact output printed by this macro should not be relied upon and is subject to future changes.
483///
484/// # Panics
485///
486/// Panics if writing to [`std::io::stderr`] fails.
487#[macro_export]
488macro_rules! dbg {
489    () => {
490        eprintln!("[{}:{}]", file!(), line!())
491    };
492    ($val:expr $(,)?) => {
493        match $val {
494            tmp => {
495                eprintln!(
496                    "[{}:{}] {} = {}",
497                    file!(),
498                    line!(),
499                    stringify!($val),
500                    $crate::pprint(&tmp)
501                );
502                tmp
503            }
504        }
505    };
506    ($($val:expr),+ $(,)?) => {
507        $crate::dbg!($($val:expr),+)
508    };
509}