pest_tmp/
span.rs

1// pest. The Elegant Parser
2// Copyright (c) 2018 DragoČ™ Tiselice
3//
4// Licensed under the Apache License, Version 2.0
5// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
6// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. All files in the project carrying such notice may not be copied,
8// modified, or distributed except according to those terms.
9
10use std::fmt;
11use std::hash::{Hash, Hasher};
12use std::ptr;
13use std::str;
14
15use position;
16
17/// A span over a `&str`. It is created from either [two `Position`s] or from a [`Pair`].
18///
19/// [two `Position`s]: struct.Position.html#method.span
20/// [`Pair`]: ../iterators/struct.Pair.html#method.span
21#[derive(Clone)]
22pub struct Span<'i> {
23    input: &'i str,
24    /// # Safety
25    ///
26    /// Must be a valid character boundary index into `input`.
27    start: usize,
28    /// # Safety
29    ///
30    /// Must be a valid character boundary index into `input`.
31    end: usize,
32}
33
34impl<'i> Span<'i> {
35    /// Create a new `Span` without checking invariants. (Checked with `debug_assertions`.)
36    ///
37    /// # Safety
38    ///
39    /// `input[start..end]` must be a valid subslice; that is, said indexing should not panic.
40    pub(crate) unsafe fn new_unchecked(input: &str, start: usize, end: usize) -> Span {
41        debug_assert!(input.get(start..end).is_some());
42        Span { input, start, end }
43    }
44
45    /// Attempts to create a new span. Will return `None` if `input[start..end]` is an invalid index
46    /// into `input`.
47    ///
48    /// # Examples
49    ///
50    /// ```
51    /// # use pest::Span;
52    /// let input = "Hello!";
53    /// assert_eq!(None, Span::new(input, 100, 0));
54    /// assert!(Span::new(input, 0, input.len()).is_some());
55    /// ```
56    #[allow(clippy::new_ret_no_self)]
57    pub fn new(input: &str, start: usize, end: usize) -> Option<Span> {
58        if input.get(start..end).is_some() {
59            Some(Span { input, start, end })
60        } else {
61            None
62        }
63    }
64
65    /// Returns the `Span`'s start byte position as a `usize`.
66    ///
67    /// # Examples
68    ///
69    /// ```
70    /// # use pest::Position;
71    /// let input = "ab";
72    /// let start = Position::from_start(input);
73    /// let end = start.clone();
74    /// let span = start.span(&end);
75    ///
76    /// assert_eq!(span.start(), 0);
77    /// ```
78    #[inline]
79    pub fn start(&self) -> usize {
80        self.start
81    }
82
83    /// Returns the `Span`'s end byte position as a `usize`.
84    ///
85    /// # Examples
86    ///
87    /// ```
88    /// # use pest::Position;
89    /// let input = "ab";
90    /// let start = Position::from_start(input);
91    /// let end = start.clone();
92    /// let span = start.span(&end);
93    ///
94    /// assert_eq!(span.end(), 0);
95    /// ```
96    #[inline]
97    pub fn end(&self) -> usize {
98        self.end
99    }
100
101    /// Returns the `Span`'s start `Position`.
102    ///
103    /// # Examples
104    ///
105    /// ```
106    /// # use pest::Position;
107    /// let input = "ab";
108    /// let start = Position::from_start(input);
109    /// let end = start.clone();
110    /// let span = start.clone().span(&end);
111    ///
112    /// assert_eq!(span.start_pos(), start);
113    /// ```
114    #[inline]
115    pub fn start_pos(&self) -> position::Position<'i> {
116        // Span's start position is always a UTF-8 border.
117        unsafe { position::Position::new_unchecked(self.input, self.start) }
118    }
119
120    /// Returns the `Span`'s end `Position`.
121    ///
122    /// # Examples
123    ///
124    /// ```
125    /// # use pest::Position;
126    /// let input = "ab";
127    /// let start = Position::from_start(input);
128    /// let end = start.clone();
129    /// let span = start.span(&end);
130    ///
131    /// assert_eq!(span.end_pos(), end);
132    /// ```
133    #[inline]
134    pub fn end_pos(&self) -> position::Position<'i> {
135        // Span's end position is always a UTF-8 border.
136        unsafe { position::Position::new_unchecked(self.input, self.end) }
137    }
138
139    /// Splits the `Span` into a pair of `Position`s.
140    ///
141    /// # Examples
142    ///
143    /// ```
144    /// # use pest::Position;
145    /// let input = "ab";
146    /// let start = Position::from_start(input);
147    /// let end = start.clone();
148    /// let span = start.clone().span(&end);
149    ///
150    /// assert_eq!(span.split(), (start, end));
151    /// ```
152    #[inline]
153    pub fn split(self) -> (position::Position<'i>, position::Position<'i>) {
154        // Span's start and end positions are always a UTF-8 borders.
155        let pos1 = unsafe { position::Position::new_unchecked(self.input, self.start) };
156        let pos2 = unsafe { position::Position::new_unchecked(self.input, self.end) };
157
158        (pos1, pos2)
159    }
160
161    /// Captures a slice from the `&str` defined by the `Span`.
162    ///
163    /// # Examples
164    ///
165    /// ```
166    /// # use pest;
167    /// # #[allow(non_camel_case_types)]
168    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
169    /// enum Rule {}
170    ///
171    /// let input = "abc";
172    /// let mut state: Box<pest::ParserState<Rule>> = pest::ParserState::new(input).skip(1).unwrap();
173    /// let start_pos = state.position().clone();
174    /// state = state.match_string("b").unwrap();
175    /// let span = start_pos.span(&state.position().clone());
176    /// assert_eq!(span.as_str(), "b");
177    /// ```
178    #[inline]
179    pub fn as_str(&self) -> &'i str {
180        // Span's start and end positions are always a UTF-8 borders.
181        &self.input[self.start..self.end]
182    }
183
184    /// Iterates over all lines (partially) covered by this span.
185    ///
186    /// # Examples
187    ///
188    /// ```
189    /// # use pest;
190    /// # #[allow(non_camel_case_types)]
191    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
192    /// enum Rule {}
193    ///
194    /// let input = "a\nb\nc";
195    /// let mut state: Box<pest::ParserState<Rule>> = pest::ParserState::new(input).skip(2).unwrap();
196    /// let start_pos = state.position().clone();
197    /// state = state.match_string("b\nc").unwrap();
198    /// let span = start_pos.span(&state.position().clone());
199    /// assert_eq!(span.lines().collect::<Vec<_>>(), vec!["b\n", "c"]);
200    /// ```
201    #[inline]
202    pub fn lines(&self) -> Lines {
203        Lines {
204            span: self,
205            pos: self.start,
206        }
207    }
208}
209
210impl<'i> fmt::Debug for Span<'i> {
211    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
212        f.debug_struct("Span")
213            .field("str", &self.as_str())
214            .field("start", &self.start)
215            .field("end", &self.end)
216            .finish()
217    }
218}
219
220impl<'i> PartialEq for Span<'i> {
221    fn eq(&self, other: &Span<'i>) -> bool {
222        ptr::eq(self.input, other.input) && self.start == other.start && self.end == other.end
223    }
224}
225
226impl<'i> Eq for Span<'i> {}
227
228impl<'i> Hash for Span<'i> {
229    fn hash<H: Hasher>(&self, state: &mut H) {
230        (self.input as *const str).hash(state);
231        self.start.hash(state);
232        self.end.hash(state);
233    }
234}
235
236/// Line iterator for Spans, created by [`Span::lines()`].
237///
238/// Iterates all lines that are at least partially covered by the span.
239///
240/// [`Span::lines()`]: struct.Span.html#method.lines
241pub struct Lines<'i> {
242    span: &'i Span<'i>,
243    pos: usize,
244}
245
246impl<'i> Iterator for Lines<'i> {
247    type Item = &'i str;
248    fn next(&mut self) -> Option<&'i str> {
249        if self.pos > self.span.end {
250            return None;
251        }
252        let pos = position::Position::new(self.span.input, self.pos)?;
253        if pos.at_end() {
254            return None;
255        }
256        let line = pos.line_of();
257        self.pos = pos.find_line_end();
258        Some(line)
259    }
260}
261
262#[cfg(test)]
263mod tests {
264    use super::*;
265
266    #[test]
267    fn split() {
268        let input = "a";
269        let start = position::Position::from_start(input);
270        let mut end = start.clone();
271
272        assert!(end.skip(1));
273
274        let span = start.clone().span(&end.clone());
275
276        assert_eq!(span.split(), (start, end));
277    }
278
279    #[test]
280    fn lines_mid() {
281        let input = "abc\ndef\nghi";
282        let span = Span::new(input, 1, 7).unwrap();
283        let lines: Vec<_> = span.lines().collect();
284        println!("{:?}", lines);
285        assert_eq!(lines.len(), 2);
286        assert_eq!(lines[0], "abc\n".to_owned());
287        assert_eq!(lines[1], "def\n".to_owned());
288    }
289
290    #[test]
291    fn lines_eof() {
292        let input = "abc\ndef\nghi";
293        let span = Span::new(input, 5, 11).unwrap();
294        assert!(span.end_pos().at_end());
295        let lines: Vec<_> = span.lines().collect();
296        println!("{:?}", lines);
297        assert_eq!(lines.len(), 2);
298        assert_eq!(lines[0], "def\n".to_owned());
299        assert_eq!(lines[1], "ghi".to_owned());
300    }
301}