use crate::support::Extrude;
pub struct LineBlockIterOptions<'b> {
pub starts_with: &'b str,
pub extrude: Option<Extrude>,
}
pub struct LineBlockIter<'a> {
starts_with: &'a str,
extrude: Option<Extrude>,
lines: std::str::Lines<'a>,
extruded_content: Vec<&'a str>,
}
impl<'a> LineBlockIter<'a> {
pub fn new(content: &'a str, options: LineBlockIterOptions<'a>) -> Self {
LineBlockIter {
starts_with: options.starts_with,
extrude: options.extrude,
lines: content.lines(),
extruded_content: Vec::new(),
}
}
fn next_block(&mut self) -> Option<String> {
let mut current_block = String::new();
let mut in_block = false;
let extrude_content = matches!(&self.extrude, Some(Extrude::Content));
for line in self.lines.by_ref() {
if line.starts_with(self.starts_with) {
in_block = true;
current_block.push_str(line);
current_block.push('\n');
} else {
if extrude_content {
self.extruded_content.push(line);
self.extruded_content.push("\n");
}
if in_block {
return Some(current_block);
}
}
}
if in_block {
return Some(current_block);
}
None
}
pub fn collect_blocks_and_extruded_content(mut self) -> (Vec<String>, String) {
let mut blocks: Vec<String> = Vec::new();
for block in self.by_ref() {
blocks.push(block);
}
let extruded_content = self.extruded_content.join("");
(blocks, extruded_content)
}
pub fn collect_remains(mut self) -> (Vec<String>, String) {
for line in self.lines.by_ref() {
if let Some(Extrude::Content) = self.extrude {
self.extruded_content.push(line);
self.extruded_content.push("\n");
}
}
(Vec::new(), self.extruded_content.join(""))
}
}
impl Iterator for LineBlockIter<'_> {
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
self.next_block()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_support_text_line_block_iter_simple() {
let content = "\
> one
> two
line1
> three
Some extruded line";
let mut iter = LineBlockIter::new(
content,
LineBlockIterOptions {
starts_with: ">",
extrude: None,
},
);
let block = iter.next().expect("First Block should be returned");
assert_eq!(block, "> one\n> two\n");
let block = iter.next().expect("Second Block should be returned");
assert_eq!(block, "> three\n");
assert!(iter.next().is_none());
assert_eq!(iter.extruded_content.len(), 0, "extruded content vec should be 0");
}
#[test]
fn test_support_text_line_block_iter_extrude_content_simple() {
let content = "\
> one
> two
line1
> three
Some extruded line";
let (blocks, extruded_content) = LineBlockIter::new(
content,
LineBlockIterOptions {
starts_with: ">",
extrude: Some(Extrude::Content),
},
)
.collect_blocks_and_extruded_content();
assert_eq!(blocks.len(), 2);
assert_eq!(blocks[0], "> one\n> two\n");
assert_eq!(blocks[1], "> three\n");
assert_eq!(extruded_content, "line1\nSome extruded line\n");
}
#[test]
fn test_support_text_line_block_iter_collect_remains() {
let content = "\
> one
> two
non-block line
> three
another remain";
let mut iter = LineBlockIter::new(
content,
LineBlockIterOptions {
starts_with: ">",
extrude: Some(Extrude::Content),
},
);
let first_block = iter.next().expect("Expected a block");
assert_eq!(first_block, "> one\n> two\n");
let (blocks, remains) = iter.collect_remains();
assert!(blocks.is_empty(), "Remaining blocks should be empty");
let expected = "non-block line\n> three\nanother remain\n";
assert_eq!(remains, expected);
}
}