sweet_potator/recipe/
reader.rs1use std::{
2 io::{self, BufRead},
3 ops::Not,
4};
5
6pub(super) struct Reader<R> {
7 lines: io::Lines<io::BufReader<R>>,
8 should_trim: bool,
9}
10
11impl<R: io::Read> Reader<R> {
12 pub fn new(reader: R, should_trim: bool) -> Self {
13 let lines = io::BufReader::new(reader).lines();
14 Self { lines, should_trim }
15 }
16
17 pub fn next_block(&mut self) -> io::Result<Option<Vec<String>>> {
18 let trim = |s: &str| (if self.should_trim { s.trim() } else { s }).to_string();
19 let block: Vec<_> = self
20 .lines
21 .by_ref()
22 .map(|line| Ok(trim(&line?)))
23 .skip_while(|res| matches!(res, Ok(line) if line.is_empty()))
24 .take_while(|res| matches!(res, Ok(line) if !line.is_empty()))
25 .collect::<io::Result<_>>()?;
26 Ok(block.is_empty().not().then_some(block))
27 }
28}
29
30#[cfg(test)]
31mod tests {
32
33 use super::*;
34
35 #[test]
36 fn test_normal() {
37 let text = [
38 "",
39 "",
40 "block 1, line 1",
41 "block 1, line 2",
42 " ",
43 "",
44 "",
45 " ",
46 "",
47 "block 2, line 1",
48 "",
49 "",
50 ]
51 .join("\n");
52 let mut reader = Reader::new(io::Cursor::new(text), false);
53 assert_eq!(
54 reader.next_block().unwrap().unwrap(),
55 vec!["block 1, line 1", "block 1, line 2", " "]
56 );
57 assert_eq!(reader.next_block().unwrap().unwrap(), vec![" "]);
58 assert_eq!(
59 reader.next_block().unwrap().unwrap(),
60 vec!["block 2, line 1"]
61 );
62 assert_eq!(reader.next_block().unwrap(), None);
63 }
64
65 #[test]
66 fn test_trimmed() {
67 let text = [" ", "block", " ", "", " ", ""].join("\n");
68 let mut reader = Reader::new(io::Cursor::new(text), true);
69 assert_eq!(reader.next_block().unwrap().unwrap(), vec!["block"]);
70 assert_eq!(reader.next_block().unwrap(), None);
71 }
72}