dbg_pls/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![warn(clippy::pedantic)]
3#![forbid(unsafe_code)]
4//! Syntax aware debug printing.
5//!
6//! Makes use of `syn` and `prettyplease` in order to provide the most
7//! canonincal rust debug lines as possible, quickly.
8//!
9//! # Example usage
10//!
11//! ```
12//! use dbg_pls::{pretty, DebugPls};
13//!
14//! #[derive(DebugPls, Copy, Clone)]
15//! pub struct Demo {
16//!     foo: i32,
17//!     bar: &'static str,
18//! }
19//!
20//! let mut val = [Demo { foo: 5, bar: "hello" }; 10];
21//! val[6].bar = "Hello, world! I am a very long string";
22//!
23//! let output = format!("{}", pretty(&val));
24//! let expected = r#"[
25//!     Demo { foo: 5, bar: "hello" },
26//!     Demo { foo: 5, bar: "hello" },
27//!     Demo { foo: 5, bar: "hello" },
28//!     Demo { foo: 5, bar: "hello" },
29//!     Demo { foo: 5, bar: "hello" },
30//!     Demo { foo: 5, bar: "hello" },
31//!     Demo {
32//!         foo: 5,
33//!         bar: "Hello, world! I am a very long string",
34//!     },
35//!     Demo { foo: 5, bar: "hello" },
36//!     Demo { foo: 5, bar: "hello" },
37//!     Demo { foo: 5, bar: "hello" },
38//! ]"#;
39//!
40//! assert_eq!(output, expected);
41//! ```
42//!
43//! # Example with highlighting
44//!
45//! ```
46//! use dbg_pls::{color, DebugPls};
47//!
48//! #[derive(DebugPls, Copy, Clone)]
49//! pub struct Demo {
50//!     foo: i32,
51//!     bar: &'static str,
52//! }
53//!
54//! let mut val = [Demo { foo: 5, bar: "hello" }; 10];
55//! val[6].bar = "Hello, world! I am a very long string";
56//!
57//! println!("{}", color(&val));
58//! ```
59//! Outputs:
60//!
61//! ![](https://raw.githubusercontent.com/conradludgate/dbg-pls/5dee03187a3f83693739e0288d56da5980e1d486/readme/highlighted.png)
62//!
63//! # Why
64//!
65//! For the sake of demonstration, let's take a look at the snippet from above.
66//! It provides an array of 10 `Demo` structs. You could imagine this to
67//! be representative of a complex deep struct.
68//!
69//! ```
70//! #[derive(Debug, Copy, Clone)]
71//! pub struct Demo {
72//!     foo: i32,
73//!     bar: &'static str,
74//! }
75//!
76//! let mut val = [Demo { foo: 5, bar: "hello" }; 10];
77//! val[6].bar = "Hello, world! I am a very long string";
78//!
79//! println!("{:?}", val);
80//! ```
81//!
82//! This outputs
83//!
84//! ```text
85//! [Demo { foo: 5, bar: "hello" }, Demo { foo: 5, bar: "hello" }, Demo { foo: 5, bar: "hello" }, Demo { foo: 5, bar: "hello" }, Demo { foo: 5, bar: "hello" }, Demo { foo: 5, bar: "hello" }, Demo { foo: 5, bar: "Hello, world! I am a very long string" }, Demo { foo: 5, bar: "hello" }, Demo { foo: 5, bar: "hello" }, Demo { foo: 5, bar: "hello" }]
86//! ```
87//!
88//! Switching to the alternative output format `{:#?}` you get the following
89//!
90//! ```text
91//! [
92//!    Demo {
93//!        foo: 5,
94//!        bar: "hello",
95//!    },
96//!    Demo {
97//!        foo: 5,
98//!        bar: "hello",
99//!    },
100//!    Demo {
101//!        foo: 5,
102//!        bar: "hello",
103//!    },
104//!    Demo {
105//!        foo: 5,
106//!        bar: "hello",
107//!    },
108//!    Demo {
109//!        foo: 5,
110//!        bar: "hello",
111//!    },
112//!    Demo {
113//!        foo: 5,
114//!        bar: "hello",
115//!    },
116//!    Demo {
117//!        foo: 5,
118//!        bar: "Hello, world! I am a very long string",
119//!    },
120//!    Demo {
121//!        foo: 5,
122//!        bar: "hello",
123//!    },
124//!    Demo {
125//!        foo: 5,
126//!        bar: "hello",
127//!    },
128//!    Demo {
129//!        foo: 5,
130//!        bar: "hello",
131//!    },
132//! ]
133//! ```
134//!
135//! Both of these are very unweildy to read through. Compare that to our `pretty` formatting:
136//!
137//! ```
138//! # use dbg_pls::pretty;
139//! # let val = 0;
140//! println!("{}", pretty(&val));
141//! ```
142//!
143//! And you will see
144//!
145//! ```text
146//! [
147//!     Demo { foo: 5, bar: "hello" },
148//!     Demo { foo: 5, bar: "hello" },
149//!     Demo { foo: 5, bar: "hello" },
150//!     Demo { foo: 5, bar: "hello" },
151//!     Demo { foo: 5, bar: "hello" },
152//!     Demo { foo: 5, bar: "hello" },
153//!     Demo {
154//!         foo: 5,
155//!         bar: "Hello, world! I am a very long string",
156//!     },
157//!     Demo { foo: 5, bar: "hello" },
158//!     Demo { foo: 5, bar: "hello" },
159//!     Demo { foo: 5, bar: "hello" },
160//! ]
161//! ```
162//!
163//! # How it works
164//!
165//! All [`DebugPls`] implementations are forced to output only valid
166//! [`syn::Expr`] values. These are then formatted using [`prettyplease::unparse`].
167//! Finally, it uses [`syntect`] to provide syntax highlighting, with theme provided by
168//! <https://github.com/jonschlinkert/sublime-monokai-extended>
169mod debug_list;
170mod debug_map;
171mod debug_set;
172mod debug_struct;
173mod debug_tuple;
174mod debug_tuple_struct;
175mod impls;
176pub use debug_list::DebugList;
177pub use debug_map::DebugMap;
178pub use debug_set::DebugSet;
179pub use debug_struct::DebugStruct;
180pub use debug_tuple::DebugTuple;
181pub use debug_tuple_struct::DebugTupleStruct;
182
183#[cfg(feature = "pretty")]
184mod pretty;
185#[cfg(feature = "pretty")]
186pub use pretty::pretty;
187
188#[cfg(feature = "colors")]
189mod colors;
190#[cfg(feature = "colors")]
191pub use colors::color;
192
193#[cfg(feature = "derive")]
194#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
195/// Derives the standard `DebugPls` implementation.
196///
197/// Works exactly like [`Debug`]
198///
199/// ```
200/// use dbg_pls::{pretty, DebugPls};
201/// #[derive(DebugPls)]
202/// struct Point {
203///     x: i32,
204///     y: i32,
205/// }
206///
207/// let origin = Point { x: 0, y: 0 };
208///
209/// assert_eq!(
210///     format!("The origin is: {}", pretty(&origin)),
211///     "The origin is: Point { x: 0, y: 0 }",
212/// );
213/// ```
214pub use dbg_pls_derive::DebugPls;
215
216#[doc(hidden)]
217pub mod __private {
218    #[cfg(feature = "colors")]
219    pub use crate::colors::ColorStr;
220    #[cfg(feature = "pretty")]
221    pub use crate::pretty::Str as PrettyStr;
222}
223
224/// Syntax aware pretty-printed debug formatting.
225///
226/// `DebugPls` should format the output in a programmer-facing, debugging context.
227///
228/// Generally speaking, you should just `derive` a `Debug` implementation.
229///
230/// # Examples
231///
232/// Deriving an implementation:
233///
234/// ```
235/// use dbg_pls::{pretty, DebugPls};
236/// #[derive(DebugPls)]
237/// struct Point {
238///     x: i32,
239///     y: i32,
240/// }
241///
242/// let origin = Point { x: 0, y: 0 };
243///
244/// assert_eq!(format!("The origin is: {}", pretty(&origin)), "The origin is: Point { x: 0, y: 0 }");
245/// ```
246///
247/// Manually implementing:
248///
249/// ```
250/// use dbg_pls::{pretty, DebugPls, Formatter};
251/// struct Point {
252///     x: i32,
253///     y: i32,
254/// }
255///
256/// impl DebugPls for Point {
257///     fn fmt(&self, f: Formatter<'_>) {
258///         f.debug_struct("Point")
259///          .field("x", &self.x)
260///          .field("y", &self.y)
261///          .finish()
262///     }
263/// }
264///
265/// let origin = Point { x: 0, y: 0 };
266///
267/// assert_eq!(format!("The origin is: {}", pretty(&origin)), "The origin is: Point { x: 0, y: 0 }");
268/// ```
269pub trait DebugPls {
270    /// Formats the value using the given formatter.
271    ///
272    /// # Examples
273    ///
274    /// ```
275    /// use dbg_pls::{pretty, DebugPls, Formatter};
276    ///
277    /// struct Position {
278    ///     longitude: f32,
279    ///     latitude: f32,
280    /// }
281    ///
282    /// impl DebugPls for Position {
283    ///     fn fmt(&self, f: Formatter<'_>) {
284    ///         f.debug_tuple()
285    ///          .field(&self.longitude)
286    ///          .field(&self.latitude)
287    ///          .finish()
288    ///     }
289    /// }
290    ///
291    /// let position = Position { longitude: 1.987, latitude: 2.983 };
292    /// assert_eq!(format!("{}", pretty(&position)), "(1.987, 2.983)");
293    /// ```
294    fn fmt(&self, f: Formatter<'_>);
295}
296
297/// Tool for formatting, used within [`DebugPls`] implementations
298#[repr(transparent)]
299pub struct Formatter<'a> {
300    expr: &'a mut syn::Expr,
301}
302
303impl<'a> Formatter<'a> {
304    pub(crate) fn process(value: &dyn DebugPls) -> syn::Expr {
305        let mut expr = syn::Expr::Verbatim(proc_macro2::TokenStream::new());
306        value.fmt(Formatter { expr: &mut expr });
307        expr
308    }
309
310    /// Writes a wrap expression into the formatter.
311    /// This is typically reserved for more advanced uses
312    pub fn write_expr(self, expr: impl Into<syn::Expr>) {
313        *self.expr = expr.into();
314    }
315
316    /// Creates a [`DebugStruct`] builder designed to assist with creation of
317    /// [`DebugPls`] implementations for structs.
318    ///
319    /// # Examples
320    ///
321    /// ```rust
322    /// use dbg_pls::{pretty, DebugPls, Formatter};
323    ///
324    /// struct Foo {
325    ///     bar: i32,
326    ///     baz: String,
327    /// }
328    ///
329    /// impl DebugPls for Foo {
330    ///     fn fmt(&self, f: Formatter) {
331    ///         f.debug_struct("Foo")
332    ///             .field("bar", &self.bar)
333    ///             .field("baz", &self.baz)
334    ///             .finish()
335    ///     }
336    /// }
337    /// let value = Foo {
338    ///     bar: 10,
339    ///     baz: "Hello World".to_string(),
340    /// };
341    /// assert_eq!(
342    ///     format!("{}", pretty(&value)),
343    ///     "Foo { bar: 10, baz: \"Hello World\" }",
344    /// );
345    /// ```
346    #[must_use]
347    pub fn debug_struct(self, name: &str) -> DebugStruct<'a> {
348        DebugStruct::new(self, name)
349    }
350
351    /// Creates a [`DebugTuple`] builder designed to assist with creation of
352    /// [`DebugPls`] implementations for tuples.
353    ///
354    /// # Examples
355    ///
356    /// ```rust
357    /// use dbg_pls::{pretty, DebugPls, Formatter};
358    ///
359    /// struct Foo(i32, String);
360    ///
361    /// impl DebugPls for Foo {
362    ///     fn fmt(&self, f: Formatter) {
363    ///         f.debug_tuple()
364    ///             .field(&self.0)
365    ///             .field(&self.1)
366    ///             .finish()
367    ///     }
368    /// }
369    ///
370    /// let value = Foo(10, "Hello".to_string());
371    /// assert_eq!(format!("{}", pretty(&value)), "(10, \"Hello\")");
372    /// ```
373    #[must_use]
374    pub fn debug_tuple(self) -> DebugTuple<'a> {
375        DebugTuple::new(self)
376    }
377
378    /// Creates a [`DebugTupleStruct`] builder designed to assist with creation of
379    /// [`DebugPls`] implementations for tuple structs.
380    ///
381    /// # Examples
382    ///
383    /// ```rust
384    /// use dbg_pls::{pretty, DebugPls, Formatter};
385    ///
386    /// struct Foo(i32, String);
387    ///
388    /// impl DebugPls for Foo {
389    ///     fn fmt(&self, f: Formatter) {
390    ///         f.debug_tuple_struct("Foo")
391    ///             .field(&self.0)
392    ///             .field(&self.1)
393    ///             .finish()
394    ///     }
395    /// }
396    ///
397    /// let value = Foo(10, "Hello".to_string());
398    /// assert_eq!(format!("{}", pretty(&value)), "Foo(10, \"Hello\")");
399    /// ```
400    #[must_use]
401    pub fn debug_tuple_struct(self, name: &str) -> DebugTupleStruct<'a> {
402        DebugTupleStruct::new(self, name)
403    }
404
405    /// Creates a [`DebugList`] builder designed to assist with creation of
406    /// [`DebugPls`] implementations for list-like structures.
407    ///
408    /// # Examples
409    ///
410    /// ```rust
411    /// use dbg_pls::{pretty, DebugPls, Formatter};
412    ///
413    /// struct Foo(Vec<i32>);
414    ///
415    /// impl DebugPls for Foo {
416    ///     fn fmt(&self, f: Formatter<'_>) {
417    ///         f.debug_list().entries(&self.0).finish()
418    ///     }
419    /// }
420    ///
421    /// let value = Foo(vec![10, 11]);
422    /// assert_eq!(format!("{}", pretty(&value)), "[10, 11]");
423    /// ```
424    #[must_use]
425    pub fn debug_list(self) -> DebugList<'a> {
426        DebugList::new(self)
427    }
428
429    /// Creates a [`DebugMap`] builder designed to assist with creation of
430    /// [`DebugPls`] implementations for maps.
431    ///
432    /// # Examples
433    ///
434    /// ```rust
435    /// use dbg_pls::{pretty, DebugPls, Formatter};
436    /// use std::collections::BTreeMap;
437    ///
438    /// struct Foo(BTreeMap<String, i32>);
439    ///
440    /// impl DebugPls for Foo {
441    ///     fn fmt(&self, f: Formatter) {
442    ///         f.debug_map().entries(&self.0).finish()
443    ///     }
444    /// }
445    /// let mut value = Foo(BTreeMap::from([
446    ///     ("Hello".to_string(), 5),
447    ///     ("World".to_string(), 10),
448    /// ]));
449    /// assert_eq!(
450    ///     format!("{}", pretty(&value)),
451    /// "{
452    ///     \"Hello\" = 5;
453    ///     \"World\" = 10;
454    /// }",
455    /// );
456    /// ```
457    #[must_use]
458    pub fn debug_map(self) -> DebugMap<'a> {
459        DebugMap::new(self)
460    }
461
462    /// Creates a [`DebugSet`] builder designed to assist with creation of
463    /// [`DebugPls`] implementations for sets.
464    ///
465    /// # Examples
466    ///
467    /// ```rust
468    /// use dbg_pls::{pretty, DebugPls, Formatter};
469    /// use std::collections::BTreeSet;
470    ///
471    /// struct Foo(BTreeSet<String>);
472    ///
473    /// impl DebugPls for Foo {
474    ///     fn fmt(&self, f: Formatter) {
475    ///         f.debug_set().entries(&self.0).finish()
476    ///     }
477    /// }
478    /// let mut value = Foo(BTreeSet::from([
479    ///     "Hello".to_string(),
480    ///     "World".to_string(),
481    /// ]));
482    /// assert_eq!(
483    ///     format!("{}", pretty(&value)),
484    /// "{
485    ///     \"Hello\";
486    ///     \"World\"
487    /// }",
488    /// );
489    /// ```
490    #[must_use]
491    pub fn debug_set(self) -> DebugSet<'a> {
492        DebugSet::new(self)
493    }
494
495    /// Writes an identifier into the formatter. Useful for unit structs/variants
496    ///
497    /// # Panics
498    /// This will panic if the name is not a valid identifier
499    ///
500    /// # Examples
501    ///
502    /// ```rust
503    /// use dbg_pls::{pretty, DebugPls, Formatter};
504    ///
505    /// struct Foo;
506    ///
507    /// impl DebugPls for Foo {
508    ///     fn fmt(&self, f: Formatter<'_>) {
509    ///         f.debug_ident("Foo");
510    ///     }
511    /// }
512    ///
513    /// assert_eq!(format!("{}", pretty(&Foo)), "Foo");
514    /// ```
515    pub fn debug_ident(self, name: &str) {
516        let path: syn::Path = syn::Ident::into(syn::parse_str(name).unwrap());
517        self.write_expr(syn::ExprPath {
518            attrs: vec![],
519            qself: None,
520            path,
521        });
522    }
523}