dbg_pls/debug_map.rs
1use crate::{DebugPls, Formatter};
2
3/// A helper designed to assist with creation of
4/// [`DebugPls`] implementations for maps.
5///
6/// # Examples
7///
8/// ```rust
9/// use dbg_pls::{pretty, DebugPls, Formatter};
10/// use std::collections::BTreeMap;
11///
12/// struct Foo(BTreeMap<String, i32>);
13///
14/// impl DebugPls for Foo {
15/// fn fmt(&self, f: Formatter) {
16/// f.debug_map().entries(&self.0).finish()
17/// }
18/// }
19/// let mut value = Foo(BTreeMap::from([
20/// ("Hello".to_string(), 5),
21/// ("World".to_string(), 10),
22/// ]));
23/// assert_eq!(
24/// format!("{}", pretty(&value)),
25/// "{
26/// \"Hello\" = 5;
27/// \"World\" = 10;
28/// }",
29/// );
30/// ```
31pub struct DebugMap<'a> {
32 formatter: Formatter<'a>,
33 set: syn::Block,
34 key: Option<syn::Expr>,
35}
36
37impl<'a> DebugMap<'a> {
38 pub(crate) fn new(formatter: Formatter<'a>) -> Self {
39 DebugMap {
40 formatter,
41 set: syn::Block {
42 brace_token: syn::token::Brace::default(),
43 stmts: vec![],
44 },
45 key: None,
46 }
47 }
48
49 /// Adds the key part to the map output.
50 ///
51 /// # Panics
52 ///
53 /// `key` must be called before `value` and each call to `key` must be followed
54 /// by a corresponding call to `value`. Otherwise this method will panic.
55 #[must_use]
56 pub fn key(mut self, key: &dyn DebugPls) -> Self {
57 assert!(
58 self.key.replace(Formatter::process(key)).is_none(),
59 "attempted to begin a new map entry without completing the previous one"
60 );
61 self
62 }
63
64 /// Adds the value part to the map output.
65 ///
66 /// # Panics
67 ///
68 /// `key` must be called before `value` and each call to `key` must be followed
69 /// by a corresponding call to `value`. Otherwise this method will panic.
70 #[must_use]
71 pub fn value(mut self, value: &dyn DebugPls) -> Self {
72 let key = self
73 .key
74 .take()
75 .expect("attempted to format a map value before its key");
76 let value = Formatter::process(value);
77 let entry = syn::ExprAssign {
78 attrs: vec![],
79 left: Box::new(key),
80 eq_token: syn::token::Eq::default(),
81 right: Box::new(value),
82 };
83 self.set.stmts.push(syn::Stmt::Expr(
84 entry.into(),
85 Some(syn::token::Semi::default()),
86 ));
87 self
88 }
89
90 /// Adds the entry to the map output.
91 #[must_use]
92 pub fn entry(self, key: &dyn DebugPls, value: &dyn DebugPls) -> Self {
93 self.key(key).value(value)
94 }
95
96 /// Adds all the entries to the map output.
97 #[must_use]
98 pub fn entries<K, V, I>(self, entries: I) -> Self
99 where
100 K: DebugPls,
101 V: DebugPls,
102 I: IntoIterator<Item = (K, V)>,
103 {
104 entries
105 .into_iter()
106 .fold(self, |f, (key, value)| f.entry(&key, &value))
107 }
108
109 /// Closes off the map.
110 pub fn finish(self) {
111 self.formatter.write_expr(syn::ExprBlock {
112 attrs: vec![],
113 label: None,
114 block: self.set,
115 });
116 }
117}