1use std::{
2 fmt::Display,
3 io::{Error, Write},
4};
5
6pub trait IterPrint {
7 fn iter_print<W, S>(self, writer: &mut W, sep: S, is_head: bool) -> Result<(), Error>
8 where
9 W: Write,
10 S: Display;
11}
12macro_rules! impl_iter_print_tuple {
13 (@impl ,) => {
14 impl IterPrint for () {
15 fn iter_print<W, S>(self, _writer: &mut W, _sep: S, _is_head: bool) -> Result<(), Error>
16 where
17 W: Write,
18 S: Display
19 {
20 Ok(())
21 }
22 }
23 };
24 (@impl $($A:ident $a:ident)?, $($B:ident $b:ident)*) => {
25 impl<$($A,)? $($B),*> IterPrint for ($($A,)? $($B),*)
26 where
27 $($A: Display,)? $($B: Display),*
28 {
29 fn iter_print<W, S>(self, writer: &mut W, sep: S, is_head: bool) -> Result<(), Error>
30 where
31 W: Write,
32 S: Display
33 {
34 let ($($a,)? $($b,)*) = self;
35 $(
36 if is_head {
37 ::std::write!(writer, "{}", $a)?;
38 } else {
39 ::std::write!(writer, "{}{}", sep, $a)?;
40 }
41 )?
42 $( ::std::write!(writer, "{}{}", sep, $b)?; )*
43 Ok(())
44 }
45 }
46 };
47 (@inc , , $C:ident $c:ident $($D:ident $d:ident)*) => {
48 impl_iter_print_tuple!(@impl ,);
49 impl_iter_print_tuple!(@inc $C $c, , $($D $d)*);
50 };
51 (@inc $A:ident $a:ident, $($B:ident $b:ident)*, $C:ident $c:ident $($D:ident $d:ident)*) => {
52 impl_iter_print_tuple!(@impl $A $a, $($B $b)*);
53 impl_iter_print_tuple!(@inc $A $a, $($B $b)* $C $c, $($D $d)*);
54 };
55 (@inc $A:ident $a:ident, $($B:ident $b:ident)*,) => {
56 impl_iter_print_tuple!(@impl $A $a, $($B $b)*);
57 };
58 ($($t:tt)*) => {
59 impl_iter_print_tuple!(@inc , , $($t)*);
60 };
61}
62impl_iter_print_tuple!(A a B b C c D d E e F f G g H h I i J j K k);
63
64#[macro_export]
84macro_rules! iter_print {
85 (@@fmt $writer:expr, $sep:expr, $is_head:expr, ($lit:literal $(, $e:expr)* $(,)?)) => {
86 if !$is_head {
87 ::std::write!($writer, "{}", $sep).expect("io error");
88 }
89 ::std::write!($writer, $lit, $($e),*).expect("io error");
90 };
91 (@@item $writer:expr, $sep:expr, $is_head:expr, $e:expr) => {
92 $crate::iter_print!(@@fmt $writer, $sep, $is_head, ("{}", $e));
93 };
94 (@@line_feed $writer:expr $(,)?) => {
95 ::std::writeln!($writer).expect("io error");
96 };
97 (@@it $writer:expr, $sep:expr, $is_head:expr, $iter:expr) => {{
98 let mut iter = $iter.into_iter();
99 if let Some(item) = iter.next() {
100 $crate::iter_print!(@@item $writer, $sep, $is_head, item);
101 }
102 for item in iter {
103 $crate::iter_print!(@@item $writer, $sep, false, item);
104 }
105 }};
106 (@@it1 $writer:expr, $sep:expr, $is_head:expr, $iter:expr) => {{
107 let mut iter = $iter.into_iter();
108 if let Some(item) = iter.next() {
109 $crate::iter_print!(@@item $writer, $sep, $is_head, item + 1);
110 }
111 for item in iter {
112 $crate::iter_print!(@@item $writer, $sep, false, item + 1);
113 }
114 }};
115 (@@cw $writer:expr, $sep:expr, $is_head:expr, ($ch:literal $iter:expr)) => {{
116 let mut iter = $iter.into_iter();
117 let b = $ch as u8;
118 if let Some(item) = iter.next() {
119 $crate::iter_print!(@@item $writer, $sep, $is_head, (item as u8 + b) as char);
120 }
121 for item in iter {
122 $crate::iter_print!(@@item $writer, $sep, false, (item as u8 + b) as char);
123 }
124 }};
125 (@@bw $writer:expr, $sep:expr, $is_head:expr, ($b:literal $iter:expr)) => {{
126 let mut iter = $iter.into_iter();
127 let b: u8 = $b;
128 if let Some(item) = iter.next() {
129 $crate::iter_print!(@@item $writer, $sep, $is_head, (item as u8 + b) as char);
130 }
131 for item in iter {
132 $crate::iter_print!(@@item $writer, $sep, false, (item as u8 + b) as char);
133 }
134 }};
135 (@@it2d $writer:expr, $sep:expr, $is_head:expr, $iter:expr) => {
136 let mut iter = $iter.into_iter();
137 if let Some(item) = iter.next() {
138 $crate::iter_print!(@@it $writer, $sep, $is_head, item);
139 }
140 for item in iter {
141 $crate::iter_print!(@@line_feed $writer);
142 $crate::iter_print!(@@it $writer, $sep, true, item);
143 }
144 };
145 (@@tup $writer:expr, $sep:expr, $is_head:expr, $tuple:expr) => {
146 IterPrint::iter_print($tuple, &mut $writer, $sep, $is_head).expect("io error");
147 };
148 (@@ittup $writer:expr, $sep:expr, $is_head:expr, $iter:expr) => {
149 let mut iter = $iter.into_iter();
150 if let Some(item) = iter.next() {
151 $crate::iter_print!(@@tup $writer, $sep, $is_head, item);
152 }
153 for item in iter {
154 $crate::iter_print!(@@line_feed $writer);
155 $crate::iter_print!(@@tup $writer, $sep, true, item);
156 }
157 };
158 (@@assert_tag item) => {};
159 (@@assert_tag it) => {};
160 (@@assert_tag it1) => {};
161 (@@assert_tag it2d) => {};
162 (@@assert_tag tup) => {};
163 (@@assert_tag ittup) => {};
164 (@@assert_tag $tag:ident) => {
165 ::std::compile_error!(::std::concat!("invalid tag in `iter_print!`: `", std::stringify!($tag), "`"));
166 };
167 (@@inner $writer:expr, $sep:expr, $is_head:expr, @sep $e:expr, $($t:tt)*) => {
168 $crate::iter_print!(@@inner $writer, $e, $is_head, $($t)*);
169 };
170 (@@inner $writer:expr, $sep:expr, $is_head:expr, @ns $($t:tt)*) => {
171 $crate::iter_print!(@@inner $writer, "", $is_head, $($t)*);
172 };
173 (@@inner $writer:expr, $sep:expr, $is_head:expr, @lf $($t:tt)*) => {
174 $crate::iter_print!(@@inner $writer, '\n', $is_head, $($t)*);
175 };
176 (@@inner $writer:expr, $sep:expr, $is_head:expr, @sp $($t:tt)*) => {
177 $crate::iter_print!(@@inner $writer, ' ', $is_head, $($t)*);
178 };
179 (@@inner $writer:expr, $sep:expr, $is_head:expr, @flush $($t:tt)*) => {
180 $writer.flush().expect("io error");
181 $crate::iter_print!(@@inner $writer, $sep, $is_head, ! $($t)*);
182 };
183 (@@inner $writer:expr, $sep:expr, $is_head:expr, @fmt $arg:tt $($t:tt)*) => {
184 $crate::iter_print!(@@fmt $writer, $sep, $is_head, $arg);
185 $crate::iter_print!(@@inner $writer, $sep, $is_head, $($t)*);
186 };
187 (@@inner $writer:expr, $sep:expr, $is_head:expr, @cw $arg:tt $($t:tt)*) => {
188 $crate::iter_print!(@@cw $writer, $sep, $is_head, $arg);
189 $crate::iter_print!(@@inner $writer, $sep, $is_head, $($t)*);
190 };
191 (@@inner $writer:expr, $sep:expr, $is_head:expr, @bw $arg:tt $($t:tt)*) => {
192 $crate::iter_print!(@@bw $writer, $sep, $is_head, $arg);
193 $crate::iter_print!(@@inner $writer, $sep, $is_head, $($t)*);
194 };
195 (@@inner $writer:expr, $sep:expr, $is_head:expr, @$tag:ident $e:expr, $($t:tt)*) => {
196 $crate::iter_print!(@@assert_tag $tag);
197 $crate::iter_print!(@@$tag $writer, $sep, $is_head, $e);
198 $crate::iter_print!(@@inner $writer, $sep, false, $($t)*);
199 };
200 (@@inner $writer:expr, $sep:expr, $is_head:expr, @$tag:ident $e:expr; $($t:tt)*) => {
201 $crate::iter_print!(@@assert_tag $tag);
202 $crate::iter_print!(@@$tag $writer, $sep, $is_head, $e);
203 $crate::iter_print!(@@line_feed $writer);
204 $crate::iter_print!(@@inner $writer, $sep, true, $($t)*);
205 };
206 (@@inner $writer:expr, $sep:expr, $is_head:expr, @$tag:ident $e:expr) => {
207 $crate::iter_print!(@@assert_tag $tag);
208 $crate::iter_print!(@@$tag $writer, $sep, $is_head, $e);
209 $crate::iter_print!(@@inner $writer, $sep, false,);
210 };
211 (@@inner $writer:expr, $sep:expr, $is_head:expr, @$tag:ident $($t:tt)*) => {
212 ::std::compile_error!(::std::concat!("invalid expr in `iter_print!`: `", std::stringify!($($t)*), "`"));
213 };
214 (@@inner $writer:expr, $sep:expr, $is_head:expr, , $($t:tt)*) => {
215 $crate::iter_print!(@@inner $writer, $sep, $is_head, $($t)*);
216 };
217 (@@inner $writer:expr, $sep:expr, $is_head:expr, ; $($t:tt)*) => {
218 $crate::iter_print!(@@line_feed $writer);
219 $crate::iter_print!(@@inner $writer, $sep, $is_head, $($t)*);
220 };
221 (@@inner $writer:expr, $sep:expr, $is_head:expr, ! $(,)?) => {};
222 (@@inner $writer:expr, $sep:expr, $is_head:expr, ! $($t:tt)*) => {
223 $crate::iter_print!(@@inner $writer, $sep, $is_head, $($t)*);
224 };
225 (@@inner $writer:expr, $sep:expr, $is_head:expr,) => {
226 $crate::iter_print!(@@line_feed $writer);
227 };
228 (@@inner $writer:expr, $sep:expr, $is_head:expr, { $($t:tt)* } $($rest:tt)*) => {
229 $crate::iter_print!(@@inner $writer, $sep, $is_head, $($t)*, !);
230 $crate::iter_print!(@@inner $writer, $sep, $is_head, $($rest)*);
231 };
232 (@@inner $writer:expr, $sep:expr, $is_head:expr, $($t:tt)*) => {
233 $crate::iter_print!(@@inner $writer, $sep, $is_head, @item $($t)*);
234 };
235 ($writer:expr, $($t:tt)*) => {{
236 $crate::iter_print!(@@inner $writer, ' ', true, $($t)*);
237 }};
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243
244 #[test]
245 fn test_iter_print() {
246 let mut buf = Vec::new();
247 iter_print!(
248 buf, 1, 2, @sep '.', 3, 4; 5, 6, @sp @it 7..=10;
249 @tup (1, 2, 3); @flush 4, @fmt ("{}?{}", 5, 6.7);
250 { @ns @it 8..=10; @lf @it 11..=13 },
251 @it2d (0..3).map(|i| (14..=15).map(move |j| i * 2 + j));
252 @ns @ittup (0..2).map(|i| (i * 2 + 20, i * 2 + 21));
253 @flush,
254 @bw (b'a' [0, 1, 2].iter().cloned());
255 @sp @it1 (0..2)
256 );
257 let expected = r#"1 2.3.4
2585.6 7 8 9 10
2591 2 3
2604 5?6.7
2618910
26211
26312
26413 14 15
26516 17
26618 19
2672021
2682223
269abc
2701 2
271"#;
272 assert_eq!(expected, String::from_utf8_lossy(&buf));
273 }
274}