fmt_tools/
fmt_map.rs

1use crate::{fmt_display, FmtDisplay};
2use core::fmt::{self, Debug, Display, Formatter};
3
4/// [`Debug`] or [`Display`] a list of `(Debug, Debug)` objects as a map.
5pub struct FmtDebugMap<F>
6where
7    F: ?Sized,
8{
9    values_fn: F,
10}
11
12impl<F> FmtDebugMap<F> {
13    const fn new(values_fn: F) -> Self {
14        Self { values_fn }
15    }
16}
17
18impl<F, I, K, V> Debug for FmtDebugMap<F>
19where
20    F: Fn() -> I + ?Sized,
21    I: IntoIterator<Item = (K, V)>,
22    K: Debug,
23    V: Debug,
24{
25    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
26        let entries = (self.values_fn)();
27
28        f.debug_map().entries(entries).finish()
29    }
30}
31
32impl<F, I, K, V> Display for FmtDebugMap<F>
33where
34    F: Fn() -> I + ?Sized,
35    I: IntoIterator<Item = (K, V)>,
36    K: Debug,
37    V: Debug,
38{
39    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
40        Debug::fmt(self, f)
41    }
42}
43
44/// [`Debug`] or [`Display`] a list of `(Display, Display)` objects as a map.
45pub struct FmtDisplayMap<F>
46where
47    F: ?Sized,
48{
49    values_fn: F,
50}
51
52impl<F> FmtDisplayMap<F> {
53    const fn new(values_fn: F) -> Self {
54        Self { values_fn }
55    }
56}
57
58impl<F, I, K, V> Debug for FmtDisplayMap<F>
59where
60    F: Fn() -> I + ?Sized,
61    I: IntoIterator<Item = (K, V)>,
62    K: Display,
63    V: Display,
64{
65    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
66        fn display_as_debug<K, V>((key, value): (K, V)) -> (FmtDisplay<K>, FmtDisplay<V>)
67        where
68            K: Display,
69            V: Display,
70        {
71            (fmt_display(key), fmt_display(value))
72        }
73
74        let entries = (self.values_fn)().into_iter().map(display_as_debug);
75
76        f.debug_map().entries(entries).finish()
77    }
78}
79
80impl<F, I, K, V> Display for FmtDisplayMap<F>
81where
82    F: Fn() -> I + ?Sized,
83    I: IntoIterator<Item = (K, V)>,
84    K: Display,
85    V: Display,
86{
87    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
88        Debug::fmt(self, f)
89    }
90}
91
92/// Creates an object that [`Debug`] or [`Display`] a list of `(Debug, Debug)` objects as a map.
93///
94/// Example:
95///
96/// ```rust
97/// let fmt = fmt_tools::fmt_debug_map(|| ('a'..'d').zip('x'..));
98///
99/// assert_eq!(format!("{fmt:?}"), "{'a': 'x', 'b': 'y', 'c': 'z'}");
100/// assert_eq!(format!("{fmt}"), "{'a': 'x', 'b': 'y', 'c': 'z'}");
101/// ```
102pub const fn fmt_debug_map<F, I, K, V>(values_fn: F) -> FmtDebugMap<F>
103where
104    F: Fn() -> I,
105    I: IntoIterator<Item = (K, V)>,
106    K: Debug,
107    V: Debug,
108{
109    FmtDebugMap::new(values_fn)
110}
111
112/// Creates an object that [`Debug`] or [`Display`] a list of `(Display, Display)` objects as a map.
113///
114/// Example:
115///
116/// ```rust
117/// let fmt = fmt_tools::fmt_display_map(|| ('a'..'d').zip('x'..));
118///
119/// assert_eq!(format!("{fmt:?}"), "{a: x, b: y, c: z}");
120/// assert_eq!(format!("{fmt}"), "{a: x, b: y, c: z}");
121/// ```
122pub const fn fmt_display_map<F, I, K, V>(values_fn: F) -> FmtDisplayMap<F>
123where
124    F: Fn() -> I,
125    I: IntoIterator<Item = (K, V)>,
126    K: Display,
127    V: Display,
128{
129    FmtDisplayMap::new(values_fn)
130}
131
132#[cfg(test)]
133mod tests {
134    use super::{FmtDebugMap, FmtDisplayMap};
135    use core::fmt::{self, Display, Formatter};
136
137    #[test]
138    fn test_debug_fmt_map() {
139        #[derive(Debug)]
140        struct Foo;
141
142        #[derive(Debug)]
143        struct Bar;
144
145        #[allow(trivial_casts)]
146        let test_cases = [
147            (&[] as &[(Foo, Bar)], "{}"),
148            (&[(Foo, Bar)], "{Foo: Bar}"),
149            (&[(Foo, Bar), (Foo, Bar)], "{Foo: Bar, Foo: Bar}"),
150            (&[(Foo, Bar), (Foo, Bar), (Foo, Bar)], "{Foo: Bar, Foo: Bar, Foo: Bar}"),
151        ];
152
153        for (values, expected) in test_cases {
154            let fmt_map = super::fmt_debug_map(|| values.iter().map(|(key, value)| (key, value)));
155            let unsized_fmt_map: &FmtDebugMap<dyn Fn() -> _> = &fmt_map;
156
157            assert_eq!(std::format!("{fmt_map:?}"), expected);
158            assert_eq!(std::format!("{fmt_map}"), expected);
159            assert_eq!(std::format!("{unsized_fmt_map:?}"), expected);
160            assert_eq!(std::format!("{unsized_fmt_map}"), expected);
161        }
162    }
163
164    #[test]
165    fn test_display_fmt_map() {
166        struct Foo;
167
168        impl Display for Foo {
169            fn fmt(&self, f: &mut Formatter) -> fmt::Result {
170                f.write_str("foo")
171            }
172        }
173
174        struct Bar;
175
176        impl Display for Bar {
177            fn fmt(&self, f: &mut Formatter) -> fmt::Result {
178                f.write_str("bar")
179            }
180        }
181
182        #[allow(trivial_casts)]
183        let test_cases = [
184            (&[] as &[(Foo, Bar)], "{}"),
185            (&[(Foo, Bar)], "{foo: bar}"),
186            (&[(Foo, Bar), (Foo, Bar)], "{foo: bar, foo: bar}"),
187            (&[(Foo, Bar), (Foo, Bar), (Foo, Bar)], "{foo: bar, foo: bar, foo: bar}"),
188        ];
189
190        for (values, expected) in test_cases {
191            let fmt_map = super::fmt_display_map(|| values.iter().map(|(key, value)| (key, value)));
192            let unsized_fmt_map: &FmtDisplayMap<dyn Fn() -> _> = &fmt_map;
193
194            assert_eq!(std::format!("{fmt_map:?}"), expected);
195            assert_eq!(std::format!("{fmt_map}"), expected);
196            assert_eq!(std::format!("{unsized_fmt_map:?}"), expected);
197            assert_eq!(std::format!("{unsized_fmt_map}"), expected);
198        }
199    }
200}