use alloc::string::String;
use core::ops::Range;
use crate::{CowStr, Event};
#[derive(Debug)]
pub struct TextMergeStream<'a, I> {
inner: TextMergeWithOffset<'a, DummyOffsets<I>>,
}
impl<'a, I> TextMergeStream<'a, I>
where
I: Iterator<Item = Event<'a>>,
{
pub fn new(iter: I) -> Self {
Self {
inner: TextMergeWithOffset::new(DummyOffsets(iter)),
}
}
}
impl<'a, I> Iterator for TextMergeStream<'a, I>
where
I: Iterator<Item = Event<'a>>,
{
type Item = Event<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|(event, _)| event)
}
}
#[derive(Debug)]
struct DummyOffsets<I>(I);
impl<'a, I> Iterator for DummyOffsets<I>
where
I: Iterator<Item = Event<'a>>,
{
type Item = (Event<'a>, Range<usize>);
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|event| (event, 0..0))
}
}
#[derive(Debug)]
pub struct TextMergeWithOffset<'a, I> {
iter: I,
last_event: Option<(Event<'a>, Range<usize>)>,
}
impl<'a, I> TextMergeWithOffset<'a, I>
where
I: Iterator<Item = (Event<'a>, Range<usize>)>,
{
pub fn new(iter: I) -> Self {
Self {
iter,
last_event: None,
}
}
pub fn inner(&self) -> &I {
&self.iter
}
}
impl<'a, I> Iterator for TextMergeWithOffset<'a, I>
where
I: Iterator<Item = (Event<'a>, Range<usize>)>,
{
type Item = (Event<'a>, Range<usize>);
fn next(&mut self) -> Option<Self::Item> {
match (self.last_event.take(), self.iter.next()) {
(
Some((Event::Text(last_text), last_offset)),
Some((Event::Text(next_text), next_offset)),
) => {
let mut string_buf: String = last_text.into_string();
string_buf.push_str(&next_text);
let mut offset = last_offset;
offset.end = next_offset.end;
loop {
match self.iter.next() {
Some((Event::Text(next_text), next_offset)) => {
string_buf.push_str(&next_text);
offset.end = next_offset.end;
}
next_event => {
self.last_event = next_event;
if string_buf.is_empty() {
break self.next();
} else {
break Some((
Event::Text(CowStr::Boxed(string_buf.into_boxed_str())),
offset,
));
}
}
}
}
}
(None, Some(next_event)) => {
self.last_event = Some(next_event);
self.next()
}
(None, None) => {
None
}
(last_event, next_event) => {
self.last_event = next_event;
last_event
}
}
}
}
#[cfg(test)]
mod test {
use alloc::vec::Vec;
use super::*;
use crate::Parser;
#[test]
fn text_merge_stream_indent() {
let source = r#"
first line
second line
"#;
let parser = TextMergeStream::new(Parser::new(source));
let text_events: Vec<_> = parser.filter(|e| matches!(e, Event::Text(_))).collect();
assert_eq!(
text_events,
[Event::Text("first line\nsecond line\n".into())]
);
}
#[test]
fn text_merge_with_offset_indent() {
let source = r#"
first line
second line
"#;
let parser = TextMergeWithOffset::new(Parser::new(source).into_offset_iter());
let text_events: Vec<_> = parser
.filter(|e| matches!(e, (Event::Text(_), _)))
.collect();
assert_eq!(
text_events,
[(Event::Text("first line\nsecond line\n".into()), 5..32)]
);
}
#[test]
fn text_merge_empty_is_discarded() {
let events = [
Event::Rule,
Event::Text("".into()),
Event::Text("".into()),
Event::Rule,
];
let result: Vec<_> = TextMergeStream::new(events.into_iter()).collect();
assert_eq!(result, [Event::Rule, Event::Rule]);
}
}