diff --git a/pest/src/lib.rs b/pest/src/lib.rs index 003135b1..d4a12e3d 100644 --- a/pest/src/lib.rs +++ b/pest/src/lib.rs @@ -77,7 +77,7 @@ extern crate serde_json; pub use crate::parser::Parser; pub use crate::parser_state::{state, Atomicity, Lookahead, MatchDir, ParseResult, ParserState}; pub use crate::position::Position; -pub use crate::span::{Lines, Span}; +pub use crate::span::{Lines, LinesSpan, Span}; pub use crate::token::Token; use core::fmt::Debug; use core::hash::Hash; diff --git a/pest/src/span.rs b/pest/src/span.rs index 15fdb943..4007049c 100644 --- a/pest/src/span.rs +++ b/pest/src/span.rs @@ -212,7 +212,7 @@ impl<'i> Span<'i> { &self.input[self.start..self.end] } - /// Iterates over all lines (partially) covered by this span. + /// Iterates over all lines (partially) covered by this span. Yielding a `&str` for each line. /// /// # Examples /// @@ -232,6 +232,30 @@ impl<'i> Span<'i> { #[inline] pub fn lines(&self) -> Lines { Lines { + inner: self.lines_span(), + } + } + + /// Iterates over all lines (partially) covered by this span. Yielding a `Span` for each line. + /// + /// # Examples + /// + /// ``` + /// # use pest; + /// # use pest::Span; + /// # #[allow(non_camel_case_types)] + /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + /// enum Rule {} + /// + /// let input = "a\nb\nc"; + /// let mut state: Box> = pest::ParserState::new(input).skip(2).unwrap(); + /// let start_pos = state.position().clone(); + /// state = state.match_string("b\nc").unwrap(); + /// let span = start_pos.span(&state.position().clone()); + /// assert_eq!(span.lines_span().collect::>(), vec![Span::new(input, 2, 4).unwrap(), Span::new(input, 4, 5).unwrap()]); + /// ``` + pub fn lines_span(&self) -> LinesSpan { + LinesSpan { span: self, pos: self.start, } @@ -264,19 +288,19 @@ impl<'i> Hash for Span<'i> { } } -/// Line iterator for Spans, created by [`Span::lines()`]. +/// Line iterator for Spans, created by [`Span::lines_span()`]. /// -/// Iterates all lines that are at least partially covered by the span. +/// Iterates all lines that are at least _partially_ covered by the span. Yielding a `Span` for each. /// -/// [`Span::lines()`]: struct.Span.html#method.lines -pub struct Lines<'i> { +/// [`Span::lines_span()`]: struct.Span.html#method.lines_span +pub struct LinesSpan<'i> { span: &'i Span<'i>, pos: usize, } -impl<'i> Iterator for Lines<'i> { - type Item = &'i str; - fn next(&mut self) -> Option<&'i str> { +impl<'i> Iterator for LinesSpan<'i> { + type Item = Span<'i>; + fn next(&mut self) -> Option { if self.pos > self.span.end { return None; } @@ -284,9 +308,27 @@ impl<'i> Iterator for Lines<'i> { if pos.at_end() { return None; } - let line = pos.line_of(); + + let line_start = pos.find_line_start(); self.pos = pos.find_line_end(); - Some(line) + + Span::new(self.span.input, line_start, self.pos) + } +} + +/// Line iterator for Spans, created by [`Span::lines()`]. +/// +/// Iterates all lines that are at least _partially_ covered by the span. Yielding a `&str` for each. +/// +/// [`Span::lines()`]: struct.Span.html#method.lines +pub struct Lines<'i> { + inner: LinesSpan<'i>, +} + +impl<'i> Iterator for Lines<'i> { + type Item = &'i str; + fn next(&mut self) -> Option { + self.inner.next().map(|span| span.as_str()) } } @@ -364,10 +406,12 @@ mod tests { let input = "abc\ndef\nghi"; let span = Span::new(input, 1, 7).unwrap(); let lines: Vec<_> = span.lines().collect(); - //println!("{:?}", lines); + let lines_span: Vec<_> = span.lines_span().map(|span| span.as_str()).collect(); + assert_eq!(lines.len(), 2); assert_eq!(lines[0], "abc\n".to_owned()); assert_eq!(lines[1], "def\n".to_owned()); + assert_eq!(lines, lines_span) // Verify parity with lines_span() } #[test] @@ -377,9 +421,30 @@ mod tests { assert!(span.end_pos().at_end()); assert_eq!(span.end(), 11); let lines: Vec<_> = span.lines().collect(); - //println!("{:?}", lines); + let lines_span: Vec<_> = span.lines_span().map(|span| span.as_str()).collect(); + assert_eq!(lines.len(), 2); assert_eq!(lines[0], "def\n".to_owned()); assert_eq!(lines[1], "ghi".to_owned()); + assert_eq!(lines, lines_span) // Verify parity with lines_span() + } + + #[test] + fn lines_span() { + let input = "abc\ndef\nghi"; + let span = Span::new(input, 1, 7).unwrap(); + let lines_span: Vec<_> = span.lines_span().collect(); + let lines: Vec<_> = span.lines().collect(); + + assert_eq!(lines_span.len(), 2); + assert_eq!(lines_span[0], Span::new(input, 0, 4).unwrap()); + assert_eq!(lines_span[1], Span::new(input, 4, 8).unwrap()); + assert_eq!( + lines_span + .iter() + .map(|span| span.as_str()) + .collect::>(), + lines + ); } }