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//! 
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}