str_sep/
lib.rs

1use std::fmt;
2
3pub struct SpaceSep<I>(pub I);
4pub struct PerLine<I>(pub I);
5pub struct StrSep<'a, I>(pub I, pub &'a str);
6
7pub struct SpaceSepUsize1<I>(pub I);
8pub struct PerLineUsize1<I>(pub I);
9pub struct StrSepUsize1<'a, I>(pub I, pub &'a str);
10
11macro_rules! impl_fmt {
12    ( $( $fmt:ident )* ) => { $(
13        #[allow(non_snake_case)]
14        fn $fmt<I, T: fmt::$fmt>(iter: I, sep: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result
15        where
16            I: IntoIterator<Item = T>,
17        {
18            let mut iter = iter.into_iter();
19            if let Some(first) = iter.by_ref().next() {
20                first.fmt(f)?;
21            }
22            iter.map(|rest| { write!(f, "{}", sep)?; rest.fmt(f) }).collect()
23        }
24
25        impl<I, T: fmt::$fmt> fmt::$fmt for SpaceSep<I>
26        where
27            I: IntoIterator<Item = T> + Clone,
28        {
29            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30                $fmt(self.0.clone(), " ", f)
31            }
32        }
33        impl<I, T: fmt::$fmt> fmt::$fmt for PerLine<I>
34        where
35            I: IntoIterator<Item = T> + Clone,
36        {
37            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38                $fmt(self.0.clone(), "\n", f)
39            }
40        }
41        impl<I, T: fmt::$fmt> fmt::$fmt for StrSep<'_, I>
42        where
43            I: IntoIterator<Item = T> + Clone,
44        {
45            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46                $fmt(self.0.clone(), self.1, f)
47            }
48        }
49    )* }
50}
51
52macro_rules! impl_fmt_usize1 {
53    ( $( $fmt:ident )* ) => { $(
54        impl<I, T> fmt::$fmt for SpaceSepUsize1<I>
55        where
56            I: IntoIterator<Item = T> + Clone,
57            T: std::ops::Add<usize, Output = usize>,
58        {
59            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60                $fmt(self.0.clone().into_iter().map(|u| u + 1), " ", f)
61            }
62        }
63        impl<I, T> fmt::$fmt for PerLineUsize1<I>
64        where
65            I: IntoIterator<Item = T> + Clone,
66            T: std::ops::Add<usize, Output = usize>,
67        {
68            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69                $fmt(self.0.clone().into_iter().map(|u| u + 1), "\n", f)
70            }
71        }
72        impl<I, T> fmt::$fmt for StrSepUsize1<'_, I>
73        where
74            I: IntoIterator<Item = T> + Clone,
75            T: std::ops::Add<usize, Output = usize>,
76        {
77            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78                $fmt(self.0.clone().into_iter().map(|u| u + 1), self.1, f)
79            }
80        }
81)* }
82}
83
84impl_fmt! { Binary Debug Display LowerExp LowerHex Octal Pointer UpperExp UpperHex }
85impl_fmt_usize1! { Debug Display LowerHex Octal UpperHex }
86
87#[test]
88fn sanity_check() {
89    let a = [0, 1, 2];
90    assert_eq!(format!("{}", StrSep(&a[..0], "_")), "");
91    assert_eq!(format!("{}", StrSep(&a[..1], "_")), "0");
92    assert_eq!(format!("{}", StrSep(&a[..2], "_")), "0_1");
93    assert_eq!(format!("{}", StrSep(&a, "_")), "0_1_2");
94
95    assert_eq!(format!("{}", SpaceSep(&a[..0])), "");
96    assert_eq!(format!("{}", SpaceSep(&a[..1])), "0");
97    assert_eq!(format!("{}", SpaceSep(&a[..2])), "0 1");
98    assert_eq!(format!("{}", SpaceSep(&a)), "0 1 2");
99
100    assert_eq!(format!("{}", PerLine(&a[..0])), "");
101    assert_eq!(format!("{}", PerLine(&a[..1])), "0");
102    assert_eq!(format!("{}", PerLine(&a[..2])), "0\n1");
103    assert_eq!(format!("{}", PerLine(&a)), "0\n1\n2");
104}
105
106#[test]
107fn iter() {
108    assert_eq!(format!("{}", SpaceSep(0..5)), "0 1 2 3 4");
109    assert_eq!(
110        format!("{}", SpaceSep([0, 1, 2].iter().map(|x| x * 100))),
111        "0 100 200"
112    );
113}
114
115#[test]
116fn formatting() {
117    let int = [3, 14, 1, 59];
118    assert_eq!(format!("({})", SpaceSep(&int)), "(3 14 1 59)");
119    assert_eq!(format!("{:2}", SpaceSep(&int)), " 3 14  1 59");
120    assert_eq!(format!("{:<2}", SpaceSep(&int)), "3  14 1  59");
121    assert_eq!(format!("{:o}", SpaceSep(&int)), "3 16 1 73");
122    assert_eq!(format!("{:x}", SpaceSep(&int)), "3 e 1 3b");
123    assert_eq!(format!("{:#04x}", SpaceSep(&int)), "0x03 0x0e 0x01 0x3b");
124    assert_eq!(
125        format!("{:08b}", SpaceSep(&int)),
126        "00000011 00001110 00000001 00111011"
127    );
128
129    let ch = ['a', '\0', '\n', char::MAX];
130    assert_eq!(format!("{}", SpaceSep(&ch)), "a \0 \n \u{10ffff}");
131    assert_eq!(format!("{:?}", SpaceSep(&ch)), r"'a' '\0' '\n' '\u{10ffff}'");
132    assert_eq!(format!("{:#?}", SpaceSep(&ch)), r"'a' '\0' '\n' '\u{10ffff}'");
133
134    let nested = [[1, 2], [3, 4]];
135    assert_eq!(
136        format!("\n{:?}", PerLine(&nested)),
137        r"
138[1, 2]
139[3, 4]"
140    );
141    assert_eq!(
142        format!("\n{:#?}", PerLine(&nested)),
143        r"
144[
145    1,
146    2,
147]
148[
149    3,
150    4,
151]"
152    );
153
154    let float = [0.1, 2.34, f64::INFINITY, f64::NAN];
155    assert_eq!(format!("{:4}", SpaceSep(&float)), " 0.1 2.34  inf  NaN");
156    assert_eq!(format!("{:0.2}", SpaceSep(&float)), "0.10 2.34 inf NaN");
157}
158
159#[test]
160fn usize1() {
161    assert_eq!(format!("{}", SpaceSepUsize1([0, 3, 1, 4, 2])), "1 4 2 5 3");
162    let a = vec![0, 3, 1, 4, 2];
163    assert_eq!(format!("{}", SpaceSepUsize1(&a)), "1 4 2 5 3");
164    assert_eq!(format!("{}", SpaceSepUsize1(a)), "1 4 2 5 3");
165}