diff --git a/src/intermediate/full_lines_iterator.rs b/src/intermediate/full_lines_iterator.rs new file mode 100644 index 0000000..d03003e --- /dev/null +++ b/src/intermediate/full_lines_iterator.rs @@ -0,0 +1,125 @@ +pub(crate) struct FullLinesIterator<'i> { + inner: Option<&'i str>, +} + +impl<'i> Iterator for FullLinesIterator<'i> { + type Item = &'i str; + + fn next(&mut self) -> Option { + if let Some(inner) = self.inner { + for (i, c) in inner.char_indices() { + if c == '\n' { + let (current_line, remainder) = inner.split_at(i + 1); + self.inner = Some(remainder); + return Some(current_line); + } + } + if inner.is_empty() { + None + } else { + let final_unterminated_line = Some(inner); + self.inner = None; + final_unterminated_line + } + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + if let Some(inner) = self.inner { + let line_count = { + inner.bytes().filter(|b| *b == b'\n').count() + + (if inner.ends_with('\n') { 0 } else { 1 }) + }; + (line_count, Some(line_count)) + } else { + (0, Some(0)) + } + } +} + +impl ExactSizeIterator for FullLinesIterator<'_> {} + +pub(crate) trait GetFullLines { + fn full_lines(&self) -> FullLinesIterator<'_>; +} + +impl<'s, S: AsRef> GetFullLines for S { + fn full_lines(&self) -> FullLinesIterator<'_> { + let inner = self.as_ref(); + FullLinesIterator { + inner: if inner.is_empty() { None } else { Some(inner) }, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_full_lines_iterator() { + { + let input = "foo\nbar\nbaz"; + assert_eq!(input.lines().count(), input.full_lines().count()); + assert_eq!(input.lines().count(), input.full_lines().len()); + assert_eq!(input.lines().collect::>(), vec!["foo", "bar", "baz"]); + assert_eq!( + input.full_lines().collect::>(), + vec!["foo\n", "bar\n", "baz"] + ); + } + + { + // Trailing newline + let input = "foo\nbar\nbaz\n"; + assert_eq!(input.lines().count(), input.full_lines().count()); + assert_eq!(input.lines().count(), input.full_lines().len()); + assert_eq!(input.lines().collect::>(), vec!["foo", "bar", "baz"]); + assert_eq!( + input.full_lines().collect::>(), + vec!["foo\n", "bar\n", "baz\n"] + ); + } + + { + // Leading newline + let input = "\nfoo\nbar\nbaz"; + assert_eq!(input.lines().count(), input.full_lines().count()); + assert_eq!(input.lines().count(), input.full_lines().len()); + assert_eq!( + input.lines().collect::>(), + vec!["", "foo", "bar", "baz"] + ); + assert_eq!( + input.full_lines().collect::>(), + vec!["\n", "foo\n", "bar\n", "baz"] + ); + } + + { + // Double newline + let input = "foo\nbar\n\nbaz"; + assert_eq!(input.lines().count(), input.full_lines().count()); + assert_eq!(input.lines().count(), input.full_lines().len()); + assert_eq!( + input.lines().collect::>(), + vec!["foo", "bar", "", "baz"] + ); + assert_eq!( + input.full_lines().collect::>(), + vec!["foo\n", "bar\n", "\n", "baz"] + ); + } + + { + // Empty + let input = ""; + assert_eq!(input.lines().count(), input.full_lines().count()); + assert_eq!(input.lines().count(), input.full_lines().len()); + assert_eq!(input.lines().collect::>(), Vec::<&str>::new()); + assert_eq!(input.full_lines().collect::>(), Vec::<&str>::new()); + } + } +} diff --git a/src/intermediate/mod.rs b/src/intermediate/mod.rs index ac1f411..0c41982 100644 --- a/src/intermediate/mod.rs +++ b/src/intermediate/mod.rs @@ -22,6 +22,7 @@ mod export_snippet; mod fixed_width_area; mod footnote_definition; mod footnote_reference; +mod full_lines_iterator; mod heading; mod horizontal_rule; mod inline_babel_call; diff --git a/src/intermediate/src_block.rs b/src/intermediate/src_block.rs index 9577179..c694ce8 100644 --- a/src/intermediate/src_block.rs +++ b/src/intermediate/src_block.rs @@ -1,7 +1,7 @@ use crate::error::CustomError; +use super::full_lines_iterator::GetFullLines; use super::registry::Registry; -use super::util::GetFullLines; #[derive(Debug)] pub(crate) struct ISrcBlock { diff --git a/src/intermediate/util.rs b/src/intermediate/util.rs index f2718dc..b482b98 100644 --- a/src/intermediate/util.rs +++ b/src/intermediate/util.rs @@ -46,129 +46,3 @@ enum CoalesceWhitespace { Normal, HasWhitespace { in_whitespace: bool, ret: String }, } - -pub(crate) struct FullLinesIterator<'i> { - inner: Option<&'i str>, -} - -impl<'i> Iterator for FullLinesIterator<'i> { - type Item = &'i str; - - fn next(&mut self) -> Option { - if let Some(inner) = self.inner { - for (i, c) in inner.char_indices() { - if c == '\n' { - let (current_line, remainder) = inner.split_at(i + 1); - self.inner = Some(remainder); - return Some(current_line); - } - } - if inner.is_empty() { - None - } else { - let final_unterminated_line = Some(inner); - self.inner = None; - final_unterminated_line - } - } else { - None - } - } - - fn size_hint(&self) -> (usize, Option) { - if let Some(inner) = self.inner { - let line_count = { - inner.bytes().filter(|b| *b == b'\n').count() - + (if inner.ends_with('\n') { 0 } else { 1 }) - }; - (line_count, Some(line_count)) - } else { - (0, Some(0)) - } - } -} - -impl ExactSizeIterator for FullLinesIterator<'_> {} - -pub(crate) trait GetFullLines { - fn full_lines(&self) -> FullLinesIterator<'_>; -} - -impl<'s, S: AsRef> GetFullLines for S { - fn full_lines(&self) -> FullLinesIterator<'_> { - let inner = self.as_ref(); - FullLinesIterator { - inner: if inner.is_empty() { None } else { Some(inner) }, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_full_lines_iterator() { - { - let input = "foo\nbar\nbaz"; - assert_eq!(input.lines().count(), input.full_lines().count()); - assert_eq!(input.lines().count(), input.full_lines().len()); - assert_eq!(input.lines().collect::>(), vec!["foo", "bar", "baz"]); - assert_eq!( - input.full_lines().collect::>(), - vec!["foo\n", "bar\n", "baz"] - ); - } - - { - // Trailing newline - let input = "foo\nbar\nbaz\n"; - assert_eq!(input.lines().count(), input.full_lines().count()); - assert_eq!(input.lines().count(), input.full_lines().len()); - assert_eq!(input.lines().collect::>(), vec!["foo", "bar", "baz"]); - assert_eq!( - input.full_lines().collect::>(), - vec!["foo\n", "bar\n", "baz\n"] - ); - } - - { - // Leading newline - let input = "\nfoo\nbar\nbaz"; - assert_eq!(input.lines().count(), input.full_lines().count()); - assert_eq!(input.lines().count(), input.full_lines().len()); - assert_eq!( - input.lines().collect::>(), - vec!["", "foo", "bar", "baz"] - ); - assert_eq!( - input.full_lines().collect::>(), - vec!["\n", "foo\n", "bar\n", "baz"] - ); - } - - { - // Double newline - let input = "foo\nbar\n\nbaz"; - assert_eq!(input.lines().count(), input.full_lines().count()); - assert_eq!(input.lines().count(), input.full_lines().len()); - assert_eq!( - input.lines().collect::>(), - vec!["foo", "bar", "", "baz"] - ); - assert_eq!( - input.full_lines().collect::>(), - vec!["foo\n", "bar\n", "\n", "baz"] - ); - } - - { - // Empty - let input = ""; - assert_eq!(input.lines().count(), input.full_lines().count()); - assert_eq!(input.lines().count(), input.full_lines().len()); - assert_eq!(input.lines().collect::>(), Vec::<&str>::new()); - assert_eq!(input.full_lines().collect::>(), Vec::<&str>::new()); - } - } -}