1#![feature(let_chains)]
2#![feature(type_alias_impl_trait)]
3
4use derive_new::new as New;
5use std::fmt::Display;
6
7#[macro_use]
8extern crate derive_builder;
9
10#[derive(Clone, Debug, Builder)]
11#[builder(derive(Debug))]
12pub struct InFormat {
13 #[builder(default = "ItemSeparator::default()")]
14 pub item_separator: ItemSeparator,
15
16 #[builder(default = "None")]
17 pub line_separator: Option<LineSeparator>,
18}
19
20#[derive(Clone, Debug, Builder)]
21#[builder(derive(Debug))]
22pub struct OutFormat {
38 #[builder(default = "None")]
39 pub span: Option<ItemSpan>,
43
44 #[builder(default = "String::from(\" \")")]
45 pub item_separator: String,
47
48 #[builder(default = "None")]
49 pub line_separator: Option<LineSeparator>,
51}
52
53#[derive(New, Clone, Copy, Debug, PartialEq, Eq)]
54pub struct ItemSpan {
56 span: usize,
62
63 pad: char,
67
68 anchor: Anchor,
72}
73
74#[derive(Clone, Copy, Debug, PartialEq, Eq)]
75pub enum Anchor {
77 Right,
79 Left,
81}
82
83#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
84pub enum ItemSeparator {
85 Explicit(String),
87 ByteCount(usize),
89}
90
91#[derive(New, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
92pub struct LineSeparator {
93 items_per_line: usize,
94 line_separator: String,
95}
96
97impl Default for ItemSeparator {
98 fn default() -> Self {
99 Self::Explicit(",".to_string())
100 }
101}
102
103impl Display for ItemSeparator {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 write!(f, "{self:?}")
106 }
107}
108
109pub fn write<'i, In, Out>(
130 istream: In,
131 mut ostream: Out,
132 format: OutFormat,
133) -> Result<(), std::io::Error>
134where
135 In: Iterator<Item = &'i str>,
136 Out: std::io::Write,
137{
138 let mut writer = ItemWriter::new(format);
139 for item in istream {
140 writer.write(item, &mut ostream)?;
141 }
142 Ok(())
143}
144
145pub type ItemIterator<'i> = impl Iterator<Item = &'i str> + std::fmt::Debug;
150
151pub fn read(input: &str, format: InFormat) -> ItemIterator {
186 ItemReader::new(input, format)
187}
188
189enum EmittingSeparator {
190 None,
191 Item,
192 Line,
193}
194
195#[derive(New)]
199pub struct ItemWriter {
200 #[new(value = "EmittingSeparator::None")]
201 separator: EmittingSeparator,
202 fmt: OutFormat,
203 #[new(value = "0")]
204 items_in_line: usize,
205}
206
207#[derive(New, Debug)]
208pub struct ItemReader<'i> {
209 input: &'i str,
210 fmt: InFormat,
211 #[new(value = "0")]
212 items_in_current_line: usize,
213}
214
215impl<'i> ItemReader<'i> {
216 pub fn next_item(&mut self, separator: ItemSeparator) -> Option<&'i str> {
217 if self.input.is_empty() {
218 None
219 } else {
220 match &separator {
221 ItemSeparator::Explicit(separator) => match self.input.split_once(separator) {
222 None => {
223 let last = self.input;
224 self.input = "";
225 Some(last)
226 }
227 Some((item, remainder)) => {
228 self.input = remainder;
229 if item.is_empty() {
230 None
231 } else {
232 Some(item)
233 }
234 }
235 },
236 ItemSeparator::ByteCount(count) => {
237 if self.input.len() >= *count {
238 let split = self.input.split_at(*count);
239 self.input = split.1;
240 Some(split.0)
241 } else {
242 self.input = "";
243 None
244 }
245 }
246 }
247 }
248 }
249}
250
251impl<'i> Iterator for ItemReader<'i> {
252 type Item = &'i str;
253 fn next(&mut self) -> Option<Self::Item> {
254 let separator = {
255 if let Some(line_separator) = &self.fmt.line_separator {
256 if self.items_in_current_line == line_separator.items_per_line - 1 {
257 self.items_in_current_line = 0;
258 ItemSeparator::Explicit(line_separator.line_separator.clone())
259 } else {
260 self.items_in_current_line += 1;
261 self.fmt.item_separator.clone()
262 }
263 } else {
264 self.fmt.item_separator.clone()
265 }
266 };
267 self.next_item(separator)
268 }
269}
270
271impl ItemWriter {
272 pub fn write<Out: std::io::Write>(
298 &mut self,
299 item: &str,
300 writer: &mut Out,
301 ) -> Result<(), std::io::Error> {
302 match self.separator {
304 EmittingSeparator::None => {}
305 EmittingSeparator::Item => {
306 writer.write_all(self.fmt.item_separator.as_bytes())?;
307 }
308 EmittingSeparator::Line => {
309 writer.write_all(
310 self.fmt
311 .line_separator
312 .as_ref()
313 .unwrap()
314 .line_separator
315 .as_bytes(),
316 )?;
317 }
318 }
319
320 let input_chars = item.chars().count();
322 if let Some(span) = self.fmt.span.as_ref() && input_chars < span.span {
323 let pad_count = span.span - input_chars;
324 let pad = String::from_iter(std::iter::repeat(span.pad).take(pad_count));
325 match span.anchor {
326 Anchor::Left => {
327 writer.write_all(item.as_bytes())?;
328 writer.write_all(pad.as_bytes())?;
329 }
330 Anchor::Right => {
331 writer.write_all(pad.as_bytes())?;
332 writer.write_all(item.as_bytes())?;
333 }
334 };
335 } else {
336 writer.write_all(item.as_bytes())?;
337 }
338
339 (self.separator, self.items_in_line) =
341 if let Some(line_separator) = self.fmt.line_separator.as_ref() {
342 if self.items_in_line + 1 < line_separator.items_per_line {
343 (EmittingSeparator::Item, self.items_in_line + 1)
344 } else {
345 (EmittingSeparator::Line, 0)
346 }
347 } else {
348 (EmittingSeparator::Item, 0)
349 };
350 Ok(())
351 }
352}
353
354#[cfg(test)]
355mod write_test {
356 use super::*;
357
358 #[test]
359 fn test() {
360 let input = ["001", "01", "1"];
361 let expected = "_001|__01;___1*";
362 let mut output = [0u8; 15];
363 output[14] = b'*';
364 let format = OutFormatBuilder::default()
365 .span(Some(ItemSpan::new(4, '_', Anchor::Right)))
366 .item_separator("|".to_string())
367 .line_separator(Some(LineSeparator::new(2, ";".to_string())))
368 .build()
369 .unwrap();
370 write(input.into_iter(), output.as_mut_slice(), format).unwrap();
371 assert_eq!(String::from_utf8(output.to_vec()).unwrap(), expected);
372 }
373
374 #[test]
375 fn example() {
376 let input = ["ππ", "πΆ", "πΌπΌπΌ"];
377 let format = OutFormatBuilder::default()
378 .span(Some(ItemSpan::new(4, 'π', Anchor::Right)))
379 .item_separator("π".to_string())
380 .line_separator(Some(LineSeparator::new(2, "π©\n".to_string())))
381 .build()
382 .unwrap();
383 let expected = "πππππππππΆπ©\nππΌπΌπΌ";
384 let mut output = vec![0u8; 100];
385 write(input.into_iter(), output.as_mut_slice(), format).unwrap();
386 let eof = output
387 .iter()
388 .position(|x| *x == 0u8)
389 .unwrap_or(output.len());
390 let output = output.split_at(eof).0;
391 assert_eq!(String::from_utf8(output.to_vec()).unwrap(), expected);
392 }
393}
394
395#[cfg(test)]
396mod read_test {
397 use super::*;
398
399 #[test]
400 fn reader_explicit() {
401 let input = "a,bb,ccc,,";
402 let mut reader = ItemReader::new(
403 input,
404 InFormatBuilder::default()
405 .item_separator(ItemSeparator::Explicit(",".to_string()))
406 .build()
407 .unwrap(),
408 );
409 assert_eq!(Some("a"), reader.next());
410 assert_eq!(Some("bb"), reader.next());
411 assert_eq!(Some("ccc"), reader.next());
412 assert_eq!(None, reader.next());
413 }
414
415 #[test]
416 fn reader_byte_count() {
417 let input = "aaaabbbbccccddd";
418 let mut reader = ItemReader::new(
419 input,
420 InFormatBuilder::default()
421 .item_separator(ItemSeparator::ByteCount(4))
422 .build()
423 .unwrap(),
424 );
425 assert_eq!(Some("aaaa"), reader.next());
426 assert_eq!(Some("bbbb"), reader.next());
427 assert_eq!(Some("cccc"), reader.next());
428 assert_eq!(None, reader.next());
429 }
430
431 #[test]
432 fn reader_explicit_multiline() {
433 let input = "aa,vvv,cccc,\nd,ee\n,a\n";
434 let mut reader = ItemReader::new(
435 input,
436 InFormatBuilder::default()
437 .item_separator(ItemSeparator::Explicit(",".to_string()))
438 .line_separator(Some(LineSeparator {
439 items_per_line: 3,
440 line_separator: "\n".to_string(),
441 }))
442 .build()
443 .unwrap(),
444 );
445 assert_eq!(Some("aa"), reader.next());
446 assert_eq!(Some("vvv"), reader.next());
447 assert_eq!(Some("cccc,"), reader.next());
448 assert_eq!(Some("d"), reader.next());
449 assert_eq!(Some("ee\n"), reader.next());
450 assert_eq!(Some("a"), reader.next());
451 assert_eq!(None, reader.next());
452 }
453
454 #[test]
455 fn reader_byte_count_multiline() {
456 let input = "aavvcc;ddeebb;";
457 let mut reader = ItemReader::new(
458 input,
459 InFormatBuilder::default()
460 .item_separator(ItemSeparator::ByteCount(2))
461 .line_separator(Some(LineSeparator {
462 items_per_line: 3,
463 line_separator: ";".to_string(),
464 }))
465 .build()
466 .unwrap(),
467 );
468 assert_eq!(Some("aa"), reader.next());
469 assert_eq!(Some("vv"), reader.next());
470 assert_eq!(Some("cc"), reader.next());
471 assert_eq!(Some("dd"), reader.next());
472 assert_eq!(Some("ee"), reader.next());
473 assert_eq!(Some("bb"), reader.next());
474 assert_eq!(None, reader.next());
475 }
476
477 #[test]
478 fn example_byte_count() {
479 let input = "aabbccdd";
480 let fmt = InFormatBuilder::default()
481 .item_separator(ItemSeparator::ByteCount(2))
482 .build()
483 .unwrap();
484 let mut it = read(input, fmt);
485 assert_eq!(Some("aa"), it.next());
486 assert_eq!(Some("bb"), it.next());
487 assert_eq!(Some("cc"), it.next());
488 assert_eq!(Some("dd"), it.next());
489 assert_eq!(None, it.next());
490 }
491
492 #[test]
493 fn example_explicit() {
494 let input = "πππSEPππSEPπSEPπΌπΌπΌ";
495 let fmt = InFormatBuilder::default()
496 .item_separator(ItemSeparator::Explicit("SEP".to_string()))
497 .build()
498 .unwrap();
499 let mut it = read(input, fmt);
500 assert_eq!(Some("πππ"), it.next());
501 assert_eq!(Some("ππ"), it.next());
502 assert_eq!(Some("π"), it.next());
503 assert_eq!(Some("πΌπΌπΌ"), it.next());
504 assert_eq!(None, it.next());
505 }
506}