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