pub fn blocks_of_lines_seperated_by<'a>( input: &'a str, on_skip: impl Fn(&'a str) -> bool, ) -> impl Iterator<Item = Vec<&'a str>> { let mut current_block = Vec::new(); let mut lines = input.lines(); std::iter::from_fn(move || { while let Some(next_line) = lines.next() { if on_skip(next_line) { if !current_block.is_empty() { let to_return = std::mem::take(&mut current_block); return Some(to_return); } } else { current_block.push(next_line); } } if !current_block.is_empty() { let to_return = std::mem::take(&mut current_block); return Some(to_return); } None }) } pub fn blocks_of_lines_seperated_by_empty_lines<'a>( input: &'a str, ) -> impl Iterator<Item = Vec<&'a str>> { blocks_of_lines_seperated_by(input, |line| line.trim().is_empty()) } #[cfg(test)] mod testing { use std::vec; use super::*; #[test] fn split_blocks_by_empty_lines() { const INPUT: &str = " 1st 2st 3st 4st 5st "; let expected = vec![vec!["1st", "2st"], vec!["3st"], vec!["4st"], vec!["5st"]]; let actual: Vec<Vec<&str>> = blocks_of_lines_seperated_by(INPUT, |line| line.trim().is_empty()).collect(); assert_eq!(expected, actual); } }