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}