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}