fmt_tools/
fmt_separated_list.rs

1use core::fmt::{self, Debug, Display, Formatter};
2
3struct Inner<F, S>
4where
5    F: ?Sized,
6{
7    separator: S,
8    values_fn: F,
9}
10
11impl<F, S> Inner<F, S>
12where
13    F: ?Sized,
14{
15    const fn new(values_fn: F, separator: S) -> Self
16    where
17        F: Sized,
18    {
19        Self { separator, values_fn }
20    }
21
22    fn fmt_with<I>(
23        &self,
24        f: &mut Formatter,
25        value_writer: impl FnOnce(&I::Item, &mut Formatter) -> fmt::Result + Copy,
26        separator_writer: impl FnOnce(&S, &mut Formatter) -> fmt::Result + Copy,
27    ) -> fmt::Result
28    where
29        F: Fn() -> I,
30        I: IntoIterator,
31    {
32        let mut iter = (self.values_fn)().into_iter();
33
34        if let Some(first) = iter.next() {
35            value_writer(&first, f)?;
36
37            for item in iter {
38                separator_writer(&self.separator, f)?;
39                value_writer(&item, f)?;
40            }
41        }
42
43        Ok(())
44    }
45}
46
47/// [`Debug`] or [`Display`] a list of [`Debug`] objects with a separator that implements [`Debug`].
48pub struct FmtSeparatedDebugList<F, S>
49where
50    F: ?Sized,
51{
52    inner: Inner<F, S>,
53}
54
55impl<F, S> FmtSeparatedDebugList<F, S> {
56    const fn new(values_fn: F, separator: S) -> Self
57    where
58        F: Sized,
59    {
60        Self {
61            inner: Inner::new(values_fn, separator),
62        }
63    }
64}
65
66impl<F, S, I> Debug for FmtSeparatedDebugList<F, S>
67where
68    F: Fn() -> I + ?Sized,
69    I: IntoIterator,
70    I::Item: Debug,
71    S: Debug,
72{
73    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
74        self.inner.fmt_with(f, I::Item::fmt, S::fmt)
75    }
76}
77
78impl<F, S, I> Display for FmtSeparatedDebugList<F, S>
79where
80    F: Fn() -> I + ?Sized,
81    I: IntoIterator,
82    I::Item: Debug,
83    S: Debug,
84{
85    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
86        Debug::fmt(self, f)
87    }
88}
89
90/// [`Debug`] or [`Display`] a list of [`Display`] objects with a separator that implements [`Display`].
91pub struct FmtSeparatedDisplayList<F, S>
92where
93    F: ?Sized,
94{
95    inner: Inner<F, S>,
96}
97
98impl<F, S> FmtSeparatedDisplayList<F, S> {
99    const fn new(values_fn: F, separator: S) -> Self
100    where
101        F: Sized,
102    {
103        Self {
104            inner: Inner::new(values_fn, separator),
105        }
106    }
107}
108
109impl<F, S, I> Debug for FmtSeparatedDisplayList<F, S>
110where
111    F: Fn() -> I + ?Sized,
112    I: IntoIterator,
113    I::Item: Display,
114    S: Display,
115{
116    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
117        self.inner.fmt_with(f, I::Item::fmt, S::fmt)
118    }
119}
120
121impl<F, S, I> Display for FmtSeparatedDisplayList<F, S>
122where
123    F: Fn() -> I + ?Sized,
124    I: IntoIterator,
125    I::Item: Display,
126    S: Display,
127{
128    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
129        Debug::fmt(self, f)
130    }
131}
132
133/// Creates an object that [`Debug`] or [`Display`] a list of [`Debug`] objects with specified separator that implements
134/// [`Debug`].
135///
136/// Example:
137///
138/// ```rust
139/// let fmt = fmt_tools::fmt_separated_debug_list(|| 'a'..'e', '*');
140///
141/// assert_eq!(format!("{fmt:?}"), "'a''*''b''*''c''*''d'");
142/// assert_eq!(format!("{fmt}"), "'a''*''b''*''c''*''d'");
143/// ```
144pub const fn fmt_separated_debug_list<F, S, I>(values_fn: F, separator: S) -> FmtSeparatedDebugList<F, S>
145where
146    F: Fn() -> I,
147    I: IntoIterator,
148    I::Item: Debug,
149    S: Debug,
150{
151    FmtSeparatedDebugList::new(values_fn, separator)
152}
153
154/// Creates an object that [`Debug`] or [`Display`] a list of [`Display`] objects with specified separator that
155/// implements [`Display`].
156///
157/// Example:
158///
159/// ```rust
160/// let fmt = fmt_tools::fmt_separated_display_list(|| 'a'..'e', '*');
161///
162/// assert_eq!(format!("{fmt:?}"), "a*b*c*d");
163/// assert_eq!(format!("{fmt}"), "a*b*c*d");
164/// ```
165pub const fn fmt_separated_display_list<F, S, I>(values_fn: F, separator: S) -> FmtSeparatedDisplayList<F, S>
166where
167    F: Fn() -> I,
168    I: IntoIterator,
169    I::Item: Display,
170    S: Display,
171{
172    FmtSeparatedDisplayList::new(values_fn, separator)
173}
174
175#[cfg(test)]
176mod tests {
177    use super::{FmtSeparatedDebugList, FmtSeparatedDisplayList};
178    use core::fmt::{self, Display, Formatter};
179
180    #[test]
181    fn test_debug_separated_list() {
182        #[derive(Debug)]
183        struct Foo;
184
185        #[derive(Debug)]
186        struct Bar;
187
188        #[allow(trivial_casts)]
189        let test_cases = [
190            (&[] as &[Foo], ""),
191            (&[Foo], "Foo"),
192            (&[Foo, Foo], "FooBarFoo"),
193            (&[Foo, Foo, Foo], "FooBarFooBarFoo"),
194        ];
195
196        for (values, expected) in test_cases {
197            let fmt = super::fmt_separated_debug_list(|| values, Bar);
198            let unsized_fmt: &FmtSeparatedDebugList<dyn Fn() -> &'static [Foo], Bar> = &fmt;
199
200            assert_eq!(std::format!("{fmt:?}"), expected);
201            assert_eq!(std::format!("{fmt}"), expected);
202            assert_eq!(std::format!("{unsized_fmt:?}"), expected);
203            assert_eq!(std::format!("{unsized_fmt}"), expected);
204        }
205    }
206
207    #[test]
208    fn test_display_separated_list() {
209        struct Foo;
210
211        impl Display for Foo {
212            fn fmt(&self, f: &mut Formatter) -> fmt::Result {
213                f.write_str("item")
214            }
215        }
216
217        struct Bar;
218
219        impl Display for Bar {
220            fn fmt(&self, f: &mut Formatter) -> fmt::Result {
221                f.write_str(", ")
222            }
223        }
224
225        #[allow(trivial_casts)]
226        let test_cases = [
227            (&[] as &[Foo], ""),
228            (&[Foo], "item"),
229            (&[Foo, Foo], "item, item"),
230            (&[Foo, Foo, Foo], "item, item, item"),
231        ];
232
233        for (values, expected) in test_cases {
234            let fmt = super::fmt_separated_display_list(|| values, Bar);
235            let unsized_fmt: &FmtSeparatedDisplayList<dyn Fn() -> &'static [Foo], Bar> = &fmt;
236
237            assert_eq!(std::format!("{fmt:?}"), expected);
238            assert_eq!(std::format!("{fmt}"), expected);
239            assert_eq!(std::format!("{unsized_fmt:?}"), expected);
240            assert_eq!(std::format!("{unsized_fmt}"), expected);
241        }
242    }
243}