Coverage Report

Created: 2025-06-02 07:01

/rust/registry/src/index.crates.io-6f17d22bba15001f/pest-2.7.10/src/span.rs
Line
Count
Source (jump to first uncovered line)
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
10
use core::fmt;
11
use core::hash::{Hash, Hasher};
12
use core::ops::{Bound, RangeBounds};
13
use core::ptr;
14
use core::str;
15
16
use crate::position;
17
18
/// A span over a `&str`. It is created from either [two `Position`s] or from a [`Pair`].
19
///
20
/// [two `Position`s]: struct.Position.html#method.span
21
/// [`Pair`]: ../iterators/struct.Pair.html#method.span
22
#[derive(Clone, Copy)]
23
pub struct Span<'i> {
24
    input: &'i str,
25
    start: usize,
26
    end: usize,
27
}
28
29
impl<'i> Span<'i> {
30
    /// Create a new `Span` without checking invariants. (Checked with `debug_assertions`.)
31
0
    pub(crate) fn new_internal(input: &str, start: usize, end: usize) -> Span<'_> {
32
0
        debug_assert!(input.get(start..end).is_some());
33
0
        Span { input, start, end }
34
0
    }
35
36
    /// Attempts to create a new span. Will return `None` if `input[start..end]` is an invalid index
37
    /// into `input`.
38
    ///
39
    /// # Examples
40
    ///
41
    /// ```
42
    /// # use pest::Span;
43
    /// let input = "Hello!";
44
    /// assert_eq!(None, Span::new(input, 100, 0));
45
    /// assert!(Span::new(input, 0, input.len()).is_some());
46
    /// ```
47
0
    pub fn new(input: &str, start: usize, end: usize) -> Option<Span<'_>> {
48
0
        if input.get(start..end).is_some() {
49
0
            Some(Span { input, start, end })
50
        } else {
51
0
            None
52
        }
53
0
    }
54
55
    /// Attempts to create a new span based on a sub-range.
56
    ///
57
    /// ```
58
    /// use pest::Span;
59
    /// let input = "Hello World!";
60
    /// let world = Span::new(input, 6, input.len()).unwrap();
61
    /// let orl = world.get(1..=3);
62
    /// assert!(orl.is_some());
63
    /// assert_eq!(orl.unwrap().as_str(), "orl");
64
    /// ```
65
    ///
66
    /// # Examples
67
0
    pub fn get(&self, range: impl RangeBounds<usize>) -> Option<Span<'i>> {
68
0
        let start = match range.start_bound() {
69
0
            Bound::Included(offset) => *offset,
70
0
            Bound::Excluded(offset) => *offset + 1,
71
0
            Bound::Unbounded => 0,
72
        };
73
0
        let end = match range.end_bound() {
74
0
            Bound::Included(offset) => *offset + 1,
75
0
            Bound::Excluded(offset) => *offset,
76
0
            Bound::Unbounded => self.as_str().len(),
77
        };
78
79
0
        self.as_str().get(start..end).map(|_| Span {
80
0
            input: self.input,
81
0
            start: self.start + start,
82
0
            end: self.start + end,
83
0
        })
84
0
    }
85
86
    /// Returns the `Span`'s start byte position as a `usize`.
87
    ///
88
    /// # Examples
89
    ///
90
    /// ```
91
    /// # use pest::Position;
92
    /// let input = "ab";
93
    /// let start = Position::from_start(input);
94
    /// let end = start.clone();
95
    /// let span = start.span(&end);
96
    ///
97
    /// assert_eq!(span.start(), 0);
98
    /// ```
99
    #[inline]
100
0
    pub fn start(&self) -> usize {
101
0
        self.start
102
0
    }
Unexecuted instantiation: <pest::span::Span>::start
Unexecuted instantiation: <pest::span::Span>::start
103
104
    /// Returns the `Span`'s end byte position as a `usize`.
105
    ///
106
    /// # Examples
107
    ///
108
    /// ```
109
    /// # use pest::Position;
110
    /// let input = "ab";
111
    /// let start = Position::from_start(input);
112
    /// let end = start.clone();
113
    /// let span = start.span(&end);
114
    ///
115
    /// assert_eq!(span.end(), 0);
116
    /// ```
117
    #[inline]
118
0
    pub fn end(&self) -> usize {
119
0
        self.end
120
0
    }
Unexecuted instantiation: <pest::span::Span>::end
Unexecuted instantiation: <pest::span::Span>::end
121
122
    /// Returns the `Span`'s start `Position`.
123
    ///
124
    /// # Examples
125
    ///
126
    /// ```
127
    /// # use pest::Position;
128
    /// let input = "ab";
129
    /// let start = Position::from_start(input);
130
    /// let end = start.clone();
131
    /// let span = start.clone().span(&end);
132
    ///
133
    /// assert_eq!(span.start_pos(), start);
134
    /// ```
135
    #[inline]
136
0
    pub fn start_pos(&self) -> position::Position<'i> {
137
0
        position::Position::new_internal(self.input, self.start)
138
0
    }
Unexecuted instantiation: <pest::span::Span>::start_pos
Unexecuted instantiation: <pest::span::Span>::start_pos
139
140
    /// Returns the `Span`'s end `Position`.
141
    ///
142
    /// # Examples
143
    ///
144
    /// ```
145
    /// # use pest::Position;
146
    /// let input = "ab";
147
    /// let start = Position::from_start(input);
148
    /// let end = start.clone();
149
    /// let span = start.span(&end);
150
    ///
151
    /// assert_eq!(span.end_pos(), end);
152
    /// ```
153
    #[inline]
154
0
    pub fn end_pos(&self) -> position::Position<'i> {
155
0
        position::Position::new_internal(self.input, self.end)
156
0
    }
Unexecuted instantiation: <pest::span::Span>::end_pos
Unexecuted instantiation: <pest::span::Span>::end_pos
157
158
    /// Splits the `Span` into a pair of `Position`s.
159
    ///
160
    /// # Examples
161
    ///
162
    /// ```
163
    /// # use pest::Position;
164
    /// let input = "ab";
165
    /// let start = Position::from_start(input);
166
    /// let end = start.clone();
167
    /// let span = start.clone().span(&end);
168
    ///
169
    /// assert_eq!(span.split(), (start, end));
170
    /// ```
171
    #[inline]
172
0
    pub fn split(self) -> (position::Position<'i>, position::Position<'i>) {
173
0
        let pos1 = position::Position::new_internal(self.input, self.start);
174
0
        let pos2 = position::Position::new_internal(self.input, self.end);
175
0
176
0
        (pos1, pos2)
177
0
    }
178
179
    /// Captures a slice from the `&str` defined by the `Span`.
180
    ///
181
    /// # Examples
182
    ///
183
    /// ```
184
    /// # use pest;
185
    /// # #[allow(non_camel_case_types)]
186
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
187
    /// enum Rule {}
188
    ///
189
    /// let input = "abc";
190
    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input).skip(1).unwrap();
191
    /// let start_pos = state.position().clone();
192
    /// state = state.match_string("b").unwrap();
193
    /// let span = start_pos.span(&state.position().clone());
194
    /// assert_eq!(span.as_str(), "b");
195
    /// ```
196
    #[inline]
197
0
    pub fn as_str(&self) -> &'i str {
198
0
        // Span's start and end positions are always a UTF-8 borders.
199
0
        &self.input[self.start..self.end]
200
0
    }
Unexecuted instantiation: <pest::span::Span>::as_str
Unexecuted instantiation: <pest::span::Span>::as_str
201
202
    /// Returns the input string of the `Span`.
203
    ///
204
    /// This function returns the input string of the `Span` as a `&str`. This is the source string
205
    /// from which the `Span` was created. The returned `&str` can be used to examine the contents of
206
    /// the `Span` or to perform further processing on the string.
207
    ///
208
    /// # Examples
209
    ///
210
    /// ```
211
    /// # use pest;
212
    /// # use pest::Span;
213
    ///
214
    /// // Example: Get input string from a span
215
    /// let input = "abc\ndef\nghi";
216
    /// let span = Span::new(input, 1, 7).unwrap();
217
    /// assert_eq!(span.get_input(), input);
218
    /// ```
219
0
    pub fn get_input(&self) -> &'i str {
220
0
        self.input
221
0
    }
222
223
    /// Iterates over all lines (partially) covered by this span. Yielding a `&str` for each line.
224
    ///
225
    /// # Examples
226
    ///
227
    /// ```
228
    /// # use pest;
229
    /// # #[allow(non_camel_case_types)]
230
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
231
    /// enum Rule {}
232
    ///
233
    /// let input = "a\nb\nc";
234
    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input).skip(2).unwrap();
235
    /// let start_pos = state.position().clone();
236
    /// state = state.match_string("b\nc").unwrap();
237
    /// let span = start_pos.span(&state.position().clone());
238
    /// assert_eq!(span.lines().collect::<Vec<_>>(), vec!["b\n", "c"]);
239
    /// ```
240
    #[inline]
241
0
    pub fn lines(&self) -> Lines<'_> {
242
0
        Lines {
243
0
            inner: self.lines_span(),
244
0
        }
245
0
    }
246
247
    /// Iterates over all lines (partially) covered by this span. Yielding a `Span` for each line.
248
    ///
249
    /// # Examples
250
    ///
251
    /// ```
252
    /// # use pest;
253
    /// # use pest::Span;
254
    /// # #[allow(non_camel_case_types)]
255
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
256
    /// enum Rule {}
257
    ///
258
    /// let input = "a\nb\nc";
259
    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input).skip(2).unwrap();
260
    /// let start_pos = state.position().clone();
261
    /// state = state.match_string("b\nc").unwrap();
262
    /// let span = start_pos.span(&state.position().clone());
263
    /// assert_eq!(span.lines_span().collect::<Vec<_>>(), vec![Span::new(input, 2, 4).unwrap(), Span::new(input, 4, 5).unwrap()]);
264
    /// ```
265
0
    pub fn lines_span(&self) -> LinesSpan<'_> {
266
0
        LinesSpan {
267
0
            span: self,
268
0
            pos: self.start,
269
0
        }
270
0
    }
271
}
272
273
impl<'i> fmt::Debug for Span<'i> {
274
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275
0
        f.debug_struct("Span")
276
0
            .field("str", &self.as_str())
277
0
            .field("start", &self.start)
278
0
            .field("end", &self.end)
279
0
            .finish()
280
0
    }
281
}
282
283
impl<'i> PartialEq for Span<'i> {
284
0
    fn eq(&self, other: &Span<'i>) -> bool {
285
0
        ptr::eq(self.input, other.input) && self.start == other.start && self.end == other.end
286
0
    }
287
}
288
289
impl<'i> Eq for Span<'i> {}
290
291
impl<'i> Hash for Span<'i> {
292
0
    fn hash<H: Hasher>(&self, state: &mut H) {
293
0
        (self.input as *const str).hash(state);
294
0
        self.start.hash(state);
295
0
        self.end.hash(state);
296
0
    }
297
}
298
299
/// Merges two spans into one.
300
///
301
/// This function merges two spans that are contiguous or overlapping into a single span
302
/// that covers the entire range of the two input spans. This is useful when you want to
303
/// aggregate information from multiple spans into a single entity.
304
///
305
/// The function checks if the input spans are overlapping or contiguous by comparing their
306
/// start and end positions. If they are, a new span is created with the minimum start position
307
/// and the maximum end position of the two input spans.
308
///
309
/// If the input spans are neither overlapping nor contiguous, the function returns None,
310
/// indicating that a merge operation was not possible.
311
///
312
/// # Examples
313
///
314
/// ```
315
/// # use pest;
316
/// # use pest::Span;
317
/// # use pest::merge_spans;
318
///
319
/// // Example 1: Contiguous spans
320
/// let input = "abc\ndef\nghi";
321
/// let span1 = Span::new(input, 1, 7).unwrap();
322
/// let span2 = Span::new(input, 7, 11).unwrap();
323
/// let merged = merge_spans(&span1, &span2).unwrap();
324
/// assert_eq!(merged, Span::new(input, 1, 11).unwrap());
325
///
326
/// // Example 2: Overlapping spans
327
/// let input = "abc\ndef\nghi";
328
/// let span1 = Span::new(input, 1, 7).unwrap();
329
/// let span2 = Span::new(input, 5, 11).unwrap();
330
/// let merged = merge_spans(&span1, &span2).unwrap();
331
/// assert_eq!(merged, Span::new(input, 1, 11).unwrap());
332
///
333
/// // Example 3: Non-contiguous spans
334
/// let input = "abc\ndef\nghi";
335
/// let span1 = Span::new(input, 1, 7).unwrap();
336
/// let span2 = Span::new(input, 8, 11).unwrap();
337
/// let merged = merge_spans(&span1, &span2);
338
/// assert!(merged.is_none());
339
/// ```
340
0
pub fn merge_spans<'i>(a: &Span<'i>, b: &Span<'i>) -> Option<Span<'i>> {
341
0
    if a.end() >= b.start() && a.start() <= b.end() {
342
        // The spans overlap or are contiguous, so they can be merged.
343
0
        Span::new(
344
0
            a.get_input(),
345
0
            core::cmp::min(a.start(), b.start()),
346
0
            core::cmp::max(a.end(), b.end()),
347
0
        )
348
    } else {
349
        // The spans don't overlap and aren't contiguous, so they can't be merged.
350
0
        None
351
    }
352
0
}
353
354
/// Line iterator for Spans, created by [`Span::lines_span()`].
355
///
356
/// Iterates all lines that are at least _partially_ covered by the span. Yielding a `Span` for each.
357
///
358
/// [`Span::lines_span()`]: struct.Span.html#method.lines_span
359
pub struct LinesSpan<'i> {
360
    span: &'i Span<'i>,
361
    pos: usize,
362
}
363
364
impl<'i> Iterator for LinesSpan<'i> {
365
    type Item = Span<'i>;
366
0
    fn next(&mut self) -> Option<Self::Item> {
367
0
        if self.pos > self.span.end {
368
0
            return None;
369
0
        }
370
0
        let pos = position::Position::new(self.span.input, self.pos)?;
371
0
        if pos.at_end() {
372
0
            return None;
373
0
        }
374
0
375
0
        let line_start = pos.find_line_start();
376
0
        self.pos = pos.find_line_end();
377
0
378
0
        Span::new(self.span.input, line_start, self.pos)
379
0
    }
380
}
381
382
/// Line iterator for Spans, created by [`Span::lines()`].
383
///
384
/// Iterates all lines that are at least _partially_ covered by the span. Yielding a `&str` for each.
385
///
386
/// [`Span::lines()`]: struct.Span.html#method.lines
387
pub struct Lines<'i> {
388
    inner: LinesSpan<'i>,
389
}
390
391
impl<'i> Iterator for Lines<'i> {
392
    type Item = &'i str;
393
0
    fn next(&mut self) -> Option<Self::Item> {
394
0
        self.inner.next().map(|span| span.as_str())
395
0
    }
396
}
397
398
#[cfg(test)]
399
mod tests {
400
    use super::*;
401
    use alloc::borrow::ToOwned;
402
    use alloc::vec::Vec;
403
404
    #[test]
405
    fn get() {
406
        let input = "abc123abc";
407
        let span = Span::new(input, 3, input.len()).unwrap();
408
        assert_eq!(span.as_str(), "123abc");
409
        assert_eq!(span.input, input);
410
411
        let span1 = span.get(..=2);
412
        assert!(span1.is_some());
413
        assert_eq!(span1.unwrap().input, input);
414
        assert_eq!(span1.unwrap().as_str(), "123");
415
416
        let span2 = span.get(..);
417
        assert!(span2.is_some());
418
        assert_eq!(span2.unwrap().input, input);
419
        assert_eq!(span2.unwrap().as_str(), "123abc");
420
421
        let span3 = span.get(3..);
422
        assert!(span3.is_some());
423
        assert_eq!(span3.unwrap().input, input);
424
        assert_eq!(span3.unwrap().as_str(), "abc");
425
426
        let span4 = span.get(0..0);
427
        assert!(span4.is_some());
428
        assert_eq!(span4.unwrap().input, input);
429
        assert_eq!(span4.unwrap().as_str(), "");
430
    }
431
432
    #[test]
433
    fn get_fails() {
434
        let input = "abc";
435
        let span = Span::new(input, 0, input.len()).unwrap();
436
437
        let span1 = span.get(0..100);
438
        assert!(span1.is_none());
439
440
        let span2 = span.get(100..200);
441
        assert!(span2.is_none());
442
    }
443
444
    #[test]
445
    fn span_comp() {
446
        let input = "abc\ndef\nghi";
447
        let span = Span::new(input, 1, 7).unwrap();
448
        let span2 = Span::new(input, 50, 51);
449
        assert!(span2.is_none());
450
        let span3 = Span::new(input, 0, 8).unwrap();
451
        assert!(span != span3);
452
    }
453
454
    #[test]
455
    fn split() {
456
        let input = "a";
457
        let start = position::Position::from_start(input);
458
        let mut end = start;
459
460
        assert!(end.skip(1));
461
462
        let span = start.clone().span(&end.clone());
463
464
        assert_eq!(span.split(), (start, end));
465
    }
466
467
    #[test]
468
    fn lines_mid() {
469
        let input = "abc\ndef\nghi";
470
        let span = Span::new(input, 1, 7).unwrap();
471
        let lines: Vec<_> = span.lines().collect();
472
        let lines_span: Vec<_> = span.lines_span().map(|span| span.as_str()).collect();
473
474
        assert_eq!(lines.len(), 2);
475
        assert_eq!(lines[0], "abc\n".to_owned());
476
        assert_eq!(lines[1], "def\n".to_owned());
477
        assert_eq!(lines, lines_span) // Verify parity with lines_span()
478
    }
479
480
    #[test]
481
    fn lines_eof() {
482
        let input = "abc\ndef\nghi";
483
        let span = Span::new(input, 5, 11).unwrap();
484
        assert!(span.end_pos().at_end());
485
        assert_eq!(span.end(), 11);
486
        let lines: Vec<_> = span.lines().collect();
487
        let lines_span: Vec<_> = span.lines_span().map(|span| span.as_str()).collect();
488
489
        assert_eq!(lines.len(), 2);
490
        assert_eq!(lines[0], "def\n".to_owned());
491
        assert_eq!(lines[1], "ghi".to_owned());
492
        assert_eq!(lines, lines_span) // Verify parity with lines_span()
493
    }
494
495
    #[test]
496
    fn lines_span() {
497
        let input = "abc\ndef\nghi";
498
        let span = Span::new(input, 1, 7).unwrap();
499
        let lines_span: Vec<_> = span.lines_span().collect();
500
        let lines: Vec<_> = span.lines().collect();
501
502
        assert_eq!(lines_span.len(), 2);
503
        assert_eq!(lines_span[0], Span::new(input, 0, 4).unwrap());
504
        assert_eq!(lines_span[1], Span::new(input, 4, 8).unwrap());
505
        assert_eq!(
506
            lines_span
507
                .iter()
508
                .map(|span| span.as_str())
509
                .collect::<Vec<_>>(),
510
            lines
511
        );
512
    }
513
514
    #[test]
515
    fn get_input_of_span() {
516
        let input = "abc\ndef\nghi";
517
        let span = Span::new(input, 1, 7).unwrap();
518
519
        assert_eq!(span.get_input(), input);
520
    }
521
522
    #[test]
523
    fn merge_contiguous() {
524
        let input = "abc\ndef\nghi";
525
        let span1 = Span::new(input, 1, 7).unwrap();
526
        let span2 = Span::new(input, 7, 11).unwrap();
527
        let merged = merge_spans(&span1, &span2).unwrap();
528
529
        assert_eq!(merged, Span::new(input, 1, 11).unwrap());
530
    }
531
532
    #[test]
533
    fn merge_overlapping() {
534
        let input = "abc\ndef\nghi";
535
        let span1 = Span::new(input, 1, 7).unwrap();
536
        let span2 = Span::new(input, 5, 11).unwrap();
537
        let merged = merge_spans(&span1, &span2).unwrap();
538
539
        assert_eq!(merged, Span::new(input, 1, 11).unwrap());
540
    }
541
542
    #[test]
543
    fn merge_non_contiguous() {
544
        let input = "abc\ndef\nghi";
545
        let span1 = Span::new(input, 1, 7).unwrap();
546
        let span2 = Span::new(input, 8, 11).unwrap();
547
        let merged = merge_spans(&span1, &span2);
548
549
        assert!(merged.is_none());
550
    }
551
}