pest_typed/
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
10//! Copied from pest/pest/src/position.rs (commit ac0aed3eecf435fd93ba575a39704aaa88a375b7)
11//! and modified.
12
13use crate::{formatter::FormatOption, line_indexer::LineIndexer, position};
14use core::{
15    fmt::{self, Write},
16    hash::{Hash, Hasher},
17    ops::{Bound, Range, RangeBounds},
18    ptr, str,
19};
20
21/// A span over a `&str`. It is created from either [two `Position`s] or from a [`Pair`].
22///
23/// [two `Position`s]: struct.Position.html#method.span
24/// [`Pair`]: ../iterators/struct.Pair.html#method.span
25#[derive(Clone, Copy)]
26pub struct Span<'i> {
27    input: &'i str,
28    /// # Safety
29    ///
30    /// Must be a valid character boundary index into `input`.
31    start: usize,
32    /// # Safety
33    ///
34    /// Must be a valid character boundary index into `input`.
35    end: usize,
36}
37
38impl<'i> Span<'i> {
39    /// Create a new `Span` without checking invariants. (Checked with `debug_assertions`.)
40    ///
41    /// # Safety
42    ///
43    /// `input[start..end]` must be a valid subslice; that is, said indexing should not panic.
44    pub(crate) unsafe fn new_unchecked(input: &'i str, start: usize, end: usize) -> Self {
45        debug_assert!(input.get(start..end).is_some());
46        Span { input, start, end }
47    }
48
49    /// Create a new span that contains the entire input.
50    ///
51    /// # Examples
52    ///
53    /// ```
54    /// # use pest_typed::Span;
55    /// let input = "Hello!";
56    /// let span = Span::new_full(input);
57    /// assert_eq!(span.as_str(), input);
58    /// ```
59    pub const fn new_full(input: &'i str) -> Self {
60        Span {
61            input,
62            start: 0,
63            end: input.len(),
64        }
65    }
66
67    /// Create a new span that points to the end of the input.
68    ///
69    /// # Examples
70    ///
71    /// ```
72    /// # use pest_typed::Span;
73    /// let input = "Hello!";
74    /// let span = Span::new_at_end(input);
75    /// assert_eq!(span.as_str(), "");
76    /// ```
77    pub const fn new_at_end(input: &'i str) -> Self {
78        Span {
79            input,
80            start: input.len(),
81            end: input.len(),
82        }
83    }
84
85    /// Attempts to create a new span. Will return `None` if `input[start..end]` is an invalid index
86    /// into `input`.
87    ///
88    /// # Examples
89    ///
90    /// ```
91    /// # use pest::Span;
92    /// let input = "Hello!";
93    /// assert_eq!(None, Span::new(input, 100, 0));
94    /// assert!(Span::new(input, 0, input.len()).is_some());
95    /// ```
96    pub fn new(input: &'i str, start: usize, end: usize) -> Option<Self> {
97        if input.get(start..end).is_some() {
98            Some(Span { input, start, end })
99        } else {
100            None
101        }
102    }
103
104    /// Attempts to create a new span based on a sub-range.
105    ///
106    /// ```
107    /// use pest::Span;
108    /// let input = "Hello World!";
109    /// let world = Span::new(input, 6, input.len()).unwrap();
110    /// let orl = world.get(1..=3);
111    /// assert!(orl.is_some());
112    /// assert_eq!(orl.unwrap().as_str(), "orl");
113    /// ```
114    ///
115    /// # Examples
116    pub fn get(&self, range: impl RangeBounds<usize>) -> Option<Self> {
117        let start = match range.start_bound() {
118            Bound::Included(offset) => *offset,
119            Bound::Excluded(offset) => *offset + 1,
120            Bound::Unbounded => 0,
121        };
122        let end = match range.end_bound() {
123            Bound::Included(offset) => *offset + 1,
124            Bound::Excluded(offset) => *offset,
125            Bound::Unbounded => self.as_str().len(),
126        };
127
128        self.as_str().get(start..end).map(|_| Span {
129            input: self.input,
130            start: self.start + start,
131            end: self.start + end,
132        })
133    }
134
135    /// Returns the `Span`'s start byte position as a `usize`.
136    ///
137    /// # Examples
138    ///
139    /// ```
140    /// # use pest::Position;
141    /// let input = "ab";
142    /// let start = Position::from_start(input);
143    /// let end = start.clone();
144    /// let span = start.span(&end);
145    ///
146    /// assert_eq!(span.start(), 0);
147    /// ```
148    #[inline]
149    pub const fn start(&self) -> usize {
150        self.start
151    }
152
153    /// Returns the `Span`'s end byte position as a `usize`.
154    ///
155    /// # Examples
156    ///
157    /// ```
158    /// # use pest::Position;
159    /// let input = "ab";
160    /// let start = Position::from_start(input);
161    /// let end = start.clone();
162    /// let span = start.span(&end);
163    ///
164    /// assert_eq!(span.end(), 0);
165    /// ```
166    #[inline]
167    pub const fn end(&self) -> usize {
168        self.end
169    }
170
171    /// Returns the `Span`'s start `Position`.
172    ///
173    /// # Examples
174    ///
175    /// ```
176    /// # use pest::Position;
177    /// let input = "ab";
178    /// let start = Position::from_start(input);
179    /// let end = start.clone();
180    /// let span = start.clone().span(&end);
181    ///
182    /// assert_eq!(span.start_pos(), start);
183    /// ```
184    #[inline]
185    pub fn start_pos(&self) -> position::Position<'i> {
186        // Span's start position is always a UTF-8 border.
187        unsafe { position::Position::new_unchecked(self.input, self.start) }
188    }
189
190    /// Returns the `Span`'s end `Position`.
191    ///
192    /// # Examples
193    ///
194    /// ```
195    /// # use pest::Position;
196    /// let input = "ab";
197    /// let start = Position::from_start(input);
198    /// let end = start.clone();
199    /// let span = start.span(&end);
200    ///
201    /// assert_eq!(span.end_pos(), end);
202    /// ```
203    #[inline]
204    pub fn end_pos(&self) -> position::Position<'i> {
205        // Span's end position is always a UTF-8 border.
206        unsafe { position::Position::new_unchecked(self.input, self.end) }
207    }
208
209    /// Splits the `Span` into a pair of `Position`s.
210    ///
211    /// # Examples
212    ///
213    /// ```
214    /// # use pest::Position;
215    /// let input = "ab";
216    /// let start = Position::from_start(input);
217    /// let end = start.clone();
218    /// let span = start.clone().span(&end);
219    ///
220    /// assert_eq!(span.split(), (start, end));
221    /// ```
222    #[inline]
223    pub fn split(self) -> (position::Position<'i>, position::Position<'i>) {
224        // Span's start and end positions are always a UTF-8 borders.
225        let pos1 = unsafe { position::Position::new_unchecked(self.input, self.start) };
226        let pos2 = unsafe { position::Position::new_unchecked(self.input, self.end) };
227
228        (pos1, pos2)
229    }
230
231    /// Captures a slice from the `&str` defined by the `Span`.
232    ///
233    /// # Examples
234    ///
235    /// ```
236    /// # use pest;
237    /// # #[allow(non_camel_case_types)]
238    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
239    /// enum Rule {}
240    ///
241    /// let input = "abc";
242    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input).skip(1).unwrap();
243    /// let start_pos = state.position().clone();
244    /// state = state.match_string("b").unwrap();
245    /// let span = start_pos.span(&state.position().clone());
246    /// assert_eq!(span.as_str(), "b");
247    /// ```
248    #[inline]
249    pub fn as_str(&self) -> &'i str {
250        // Span's start and end positions are always a UTF-8 borders.
251        &self.input[self.start..self.end]
252    }
253
254    /// Returns the input string of the `Span`.
255    ///
256    /// This function returns the input string of the `Span` as a `&str`. This is the source string
257    /// from which the `Span` was created. The returned `&str` can be used to examine the contents of
258    /// the `Span` or to perform further processing on the string.
259    ///
260    /// # Examples
261    ///
262    /// ```
263    /// # use pest;
264    /// # use pest::Span;
265    ///
266    /// // Example: Get input string from a span
267    /// let input = "abc\ndef\nghi";
268    /// let span = Span::new(input, 1, 7).unwrap();
269    /// assert_eq!(span.get_input(), input);
270    /// ```
271    pub const fn get_input(&self) -> &'i str {
272        self.input
273    }
274
275    /// Iterates over all lines (partially) covered by this span. Yielding a `&str` for each line.
276    ///
277    /// # Examples
278    ///
279    /// ```
280    /// # use pest;
281    /// # #[allow(non_camel_case_types)]
282    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
283    /// enum Rule {}
284    ///
285    /// let input = "a\nb\nc";
286    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input).skip(2).unwrap();
287    /// let start_pos = state.position().clone();
288    /// state = state.match_string("b\nc").unwrap();
289    /// let span = start_pos.span(&state.position().clone());
290    /// assert_eq!(span.lines().collect::<Vec<_>>(), vec!["b\n", "c"]);
291    /// ```
292    #[inline]
293    pub const fn lines<L: LineIndexer<'i>>(&self, indexer: L) -> Lines<'_, 'i, L> {
294        Lines {
295            inner: self.lines_span(indexer),
296        }
297    }
298
299    /// Iterates over all lines (partially) covered by this span. Yielding a `Span` for each line.
300    ///
301    /// # Examples
302    ///
303    /// ```
304    /// # use pest;
305    /// # use pest::Span;
306    /// # #[allow(non_camel_case_types)]
307    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
308    /// enum Rule {}
309    ///
310    /// let input = "a\nb\nc";
311    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input).skip(2).unwrap();
312    /// let start_pos = state.position().clone();
313    /// state = state.match_string("b\nc").unwrap();
314    /// let span = start_pos.span(&state.position().clone());
315    /// assert_eq!(span.lines_span().collect::<Vec<_>>(), vec![Span::new(input, 2, 4).unwrap(), Span::new(input, 4, 5).unwrap()]);
316    /// ```
317    pub const fn lines_span<L: LineIndexer<'i>>(&self, indexer: L) -> LinesSpan<'_, 'i, L> {
318        LinesSpan {
319            span: self,
320            indexer,
321            pos: self.start,
322        }
323    }
324
325    /// Skips `n` `char`s from the `Span` and returns `true` if the skip was possible or `false`
326    /// otherwise. If the return value is `false`, `start` will not be updated.
327    #[inline]
328    #[allow(dead_code)]
329    pub(crate) fn skip(&mut self, n: usize) -> bool {
330        let skipped = {
331            let mut len = 0;
332            // Position's pos is always a UTF-8 border.
333            let mut chars = self.input[self.start..self.end].chars();
334            for _ in 0..n {
335                if let Some(c) = chars.next() {
336                    len += c.len_utf8();
337                } else {
338                    return false;
339                }
340            }
341            len
342        };
343
344        self.start += skipped;
345        true
346    }
347
348    /// Skips until one of the given `strings` is found. If none of the `strings` can be found,
349    /// this function will return `false` but its `pos` will *still* be updated.
350    #[inline]
351    #[allow(dead_code, unexpected_cfgs)]
352    pub(crate) fn skip_until(&mut self, strings: &[&str]) -> bool {
353        #[cfg(not(feature = "memchr"))]
354        {
355            self.skip_until_basic(strings)
356        }
357        #[cfg(feature = "memchr")]
358        {
359            match strings {
360                [] => (),
361                [s1] => {
362                    if let Some(from) = memchr::memmem::find(
363                        &self.input.as_bytes()[self.start..self.end],
364                        s1.as_bytes(),
365                    ) {
366                        self.start += from;
367                        return true;
368                    }
369                }
370                [s1, s2] if !s1.is_empty() && !s2.is_empty() => {
371                    let b1 = s1.as_bytes()[0];
372                    let b2 = s2.as_bytes()[0];
373                    let miter = memchr::memchr2_iter(b1, b2, &self.input.as_bytes()[self.pos..]);
374                    for from in miter {
375                        let start = &self.input[self.pos + from..];
376                        if start.starts_with(s1) || start.starts_with(s2) {
377                            self.pos += from;
378                            return true;
379                        }
380                    }
381                }
382                [s1, s2, s3] if !s1.is_empty() && !s2.is_empty() && s3.is_empty() => {
383                    let b1 = s1.as_bytes()[0];
384                    let b2 = s2.as_bytes()[0];
385                    let b3 = s2.as_bytes()[0];
386                    let miter =
387                        memchr::memchr3_iter(b1, b2, b3, &self.input.as_bytes()[self.pos..]);
388                    for from in miter {
389                        let start = &self.input[self.pos + from..];
390                        if start.starts_with(s1) || start.starts_with(s2) || start.starts_with(s3) {
391                            self.pos += from;
392                            return true;
393                        }
394                    }
395                }
396                _ => {
397                    return self.skip_until_basic(strings);
398                }
399            }
400            self.pos = self.input.len();
401            false
402        }
403    }
404
405    #[inline]
406    fn skip_until_basic(&mut self, strings: &[&str]) -> bool {
407        // TODO: optimize with Aho-Corasick, e.g. https://crates.io/crates/daachorse?
408        for from in self.start..self.end {
409            let bytes = if let Some(string) = self.input.get(from..) {
410                string.as_bytes()
411            } else {
412                continue;
413            };
414
415            for slice in strings.iter() {
416                let to = slice.len();
417                if Some(slice.as_bytes()) == bytes.get(0..to) {
418                    self.start = from;
419                    return true;
420                }
421            }
422        }
423
424        self.start = self.end;
425        false
426    }
427
428    /// Matches the char at the `Position` against a specified character and returns `true` if a match
429    /// was made. If no match was made, returns `false`.
430    /// `pos` will not be updated in either case.
431    #[inline]
432    #[allow(dead_code)]
433    pub(crate) fn match_char(&self, c: char) -> bool {
434        matches!(self.input[self.start..self.end].chars().next(), Some(cc) if c == cc)
435    }
436
437    /// Matches the char at the `Position` against a filter function and returns `true` if a match
438    /// was made. If no match was made, returns `false` and `pos` will not be updated.
439    #[inline]
440    #[allow(dead_code)]
441    pub(crate) fn match_char_by<F>(&mut self, f: F) -> bool
442    where
443        F: FnOnce(char) -> bool,
444    {
445        if let Some(c) = self.input[self.start..self.end].chars().next() {
446            if f(c) {
447                self.start += c.len_utf8();
448                true
449            } else {
450                false
451            }
452        } else {
453            false
454        }
455    }
456
457    /// Matches `string` from the `Position` and returns `true` if a match was made or `false`
458    /// otherwise. If no match was made, `pos` will not be updated.
459    #[inline]
460    #[allow(dead_code)]
461    pub(crate) fn match_string(&mut self, string: &str) -> bool {
462        let to = self.start + string.len();
463
464        if self.end < to {
465            false
466        } else if Some(string.as_bytes()) == self.input.as_bytes().get(self.start..to) {
467            self.start = to;
468            true
469        } else {
470            false
471        }
472    }
473
474    /// Case-insensitively matches `string` from the `Position` and returns `true` if a match was
475    /// made or `false` otherwise. If no match was made, `pos` will not be updated.
476    #[inline]
477    #[allow(dead_code)]
478    pub(crate) fn match_insensitive(&mut self, string: &str) -> bool {
479        let matched = {
480            let slice = &self.input[self.start..self.end];
481            if let Some(slice) = slice.get(0..string.len()) {
482                slice.eq_ignore_ascii_case(string)
483            } else {
484                false
485            }
486        };
487
488        if matched {
489            self.start += string.len();
490            true
491        } else {
492            false
493        }
494    }
495
496    /// Matches `char` `range` from the `Position` and returns `true` if a match was made or `false`
497    /// otherwise. If no match was made, `pos` will not be updated.
498    #[inline]
499    #[allow(dead_code)]
500    pub(crate) fn match_range(&mut self, range: Range<char>) -> bool {
501        if let Some(c) = self.input[self.start..self.end].chars().next() {
502            if range.start <= c && c <= range.end {
503                self.start += c.len_utf8();
504                return true;
505            }
506        }
507
508        false
509    }
510}
511
512impl fmt::Debug for Span<'_> {
513    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
514        f.debug_struct("Span")
515            .field("str", &self.as_str())
516            .field("start", &self.start)
517            .field("end", &self.end)
518            .finish()
519    }
520}
521
522impl PartialEq for Span<'_> {
523    fn eq(&self, other: &Self) -> bool {
524        ptr::eq::<str>(self.input, other.input)
525            && self.start == other.start
526            && self.end == other.end
527    }
528}
529
530impl Eq for Span<'_> {}
531
532impl Hash for Span<'_> {
533    fn hash<H: Hasher>(&self, state: &mut H) {
534        (self.input as *const str).hash(state);
535        self.start.hash(state);
536        self.end.hash(state);
537    }
538}
539
540impl<'i> Span<'i> {
541    /// Format span with given option.
542    #[inline]
543    pub fn display<L, Writer, SF, MF, NF>(
544        &self,
545        indexer: L,
546        f: &mut Writer,
547        opt: FormatOption<SF, MF, NF>,
548    ) -> fmt::Result
549    where
550        L: LineIndexer<'i>,
551        Writer: Write,
552        SF: FnMut(&str, &mut Writer) -> fmt::Result,
553        MF: FnMut(&str, &mut Writer) -> fmt::Result,
554        NF: FnMut(&str, &mut Writer) -> fmt::Result,
555    {
556        opt.display_span(self, indexer, f)
557    }
558}
559
560impl fmt::Display for Span<'_> {
561    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
562        let opt = FormatOption::default();
563        opt.display_span(self, (), f)
564    }
565}
566
567/// Merges two spans into one.
568///
569/// This function merges two spans that are contiguous or overlapping into a single span
570/// that covers the entire range of the two input spans. This is useful when you want to
571/// aggregate information from multiple spans into a single entity.
572///
573/// The function checks if the input spans are overlapping or contiguous by comparing their
574/// start and end positions. If they are, a new span is created with the minimum start position
575/// and the maximum end position of the two input spans.
576///
577/// If the input spans are neither overlapping nor contiguous, the function returns None,
578/// indicating that a merge operation was not possible.
579///
580/// # Examples
581///
582/// ```
583/// # use pest;
584/// # use pest::Span;
585/// # use pest::merge_spans;
586///
587/// // Example 1: Contiguous spans
588/// let input = "abc\ndef\nghi";
589/// let span1 = Span::new(input, 1, 7).unwrap();
590/// let span2 = Span::new(input, 7, 11).unwrap();
591/// let merged = merge_spans(&span1, &span2).unwrap();
592/// assert_eq!(merged, Span::new(input, 1, 11).unwrap());
593///
594/// // Example 2: Overlapping spans
595/// let input = "abc\ndef\nghi";
596/// let span1 = Span::new(input, 1, 7).unwrap();
597/// let span2 = Span::new(input, 5, 11).unwrap();
598/// let merged = merge_spans(&span1, &span2).unwrap();
599/// assert_eq!(merged, Span::new(input, 1, 11).unwrap());
600///
601/// // Example 3: Non-contiguous spans
602/// let input = "abc\ndef\nghi";
603/// let span1 = Span::new(input, 1, 7).unwrap();
604/// let span2 = Span::new(input, 8, 11).unwrap();
605/// let merged = merge_spans(&span1, &span2);
606/// assert!(merged.is_none());
607/// ```
608pub fn merge_spans<'i>(a: &Span<'i>, b: &Span<'i>) -> Option<Span<'i>> {
609    if a.end() >= b.start() && a.start() <= b.end() {
610        // The spans overlap or are contiguous, so they can be merged.
611        Span::new(
612            a.get_input(),
613            core::cmp::min(a.start(), b.start()),
614            core::cmp::max(a.end(), b.end()),
615        )
616    } else {
617        // The spans don't overlap and aren't contiguous, so they can't be merged.
618        None
619    }
620}
621
622/// Line iterator for Spans, created by [`Span::lines_span()`].
623///
624/// Iterates all lines that are at least _partially_ covered by the span. Yielding a `Span` for each.
625///
626/// [`Span::lines_span()`]: struct.Span.html#method.lines_span
627pub struct LinesSpan<'s, 'i, L> {
628    span: &'s Span<'i>,
629    indexer: L,
630    pos: usize,
631}
632
633impl<'i, L: LineIndexer<'i>> Iterator for LinesSpan<'_, 'i, L> {
634    type Item = Span<'i>;
635    fn next(&mut self) -> Option<Self::Item> {
636        if self.pos > self.span.end {
637            return None;
638        }
639        let pos = position::Position::new(self.span.input, self.pos)?;
640        if pos.at_end() {
641            return None;
642        }
643
644        let line_start = pos.find_line_start(&self.indexer);
645        self.pos = pos.find_line_end(&self.indexer);
646
647        Span::new(self.span.input, line_start, self.pos)
648    }
649}
650
651/// Line iterator for Spans, created by [`Span::lines()`].
652///
653/// Iterates all lines that are at least _partially_ covered by the span. Yielding a `&str` for each.
654///
655/// [`Span::lines()`]: struct.Span.html#method.lines
656pub struct Lines<'s, 'i, L> {
657    inner: LinesSpan<'s, 'i, L>,
658}
659
660impl<'i, L: LineIndexer<'i>> Iterator for Lines<'_, 'i, L> {
661    type Item = &'i str;
662    fn next(&mut self) -> Option<Self::Item> {
663        self.inner.next().map(|span| span.as_str())
664    }
665}
666
667#[cfg(test)]
668mod tests {
669    use super::*;
670    use alloc::{borrow::ToOwned, vec::Vec};
671
672    #[test]
673    fn get() {
674        let input = "abc123abc";
675        let span = Span::new(input, 3, input.len()).unwrap();
676        assert_eq!(span.as_str(), "123abc");
677        assert_eq!(span.input, input);
678
679        let span1 = span.get(..=2);
680        assert!(span1.is_some());
681        assert_eq!(span1.unwrap().input, input);
682        assert_eq!(span1.unwrap().as_str(), "123");
683
684        let span2 = span.get(..);
685        assert!(span2.is_some());
686        assert_eq!(span2.unwrap().input, input);
687        assert_eq!(span2.unwrap().as_str(), "123abc");
688
689        let span3 = span.get(3..);
690        assert!(span3.is_some());
691        assert_eq!(span3.unwrap().input, input);
692        assert_eq!(span3.unwrap().as_str(), "abc");
693
694        let span4 = span.get(0..0);
695        assert!(span4.is_some());
696        assert_eq!(span4.unwrap().input, input);
697        assert_eq!(span4.unwrap().as_str(), "");
698    }
699
700    #[test]
701    fn get_fails() {
702        let input = "abc";
703        let span = Span::new(input, 0, input.len()).unwrap();
704
705        let span1 = span.get(0..100);
706        assert!(span1.is_none());
707
708        let span2 = span.get(100..200);
709        assert!(span2.is_none());
710    }
711
712    #[test]
713    fn span_comp() {
714        let input = "abc\ndef\nghi";
715        let span = Span::new(input, 1, 7).unwrap();
716        let span2 = Span::new(input, 50, 51);
717        assert!(span2.is_none());
718        let span3 = Span::new(input, 0, 8).unwrap();
719        assert!(span != span3);
720    }
721
722    #[test]
723    fn split() {
724        let input = "a";
725        let start = position::Position::from_start(input);
726        let mut end = start;
727
728        assert!(end.skip(1));
729
730        let span = start.clone().span(&end.clone());
731
732        assert_eq!(span.split(), (start, end));
733    }
734
735    #[test]
736    fn lines_mid() {
737        let input = "abc\ndef\nghi";
738        let span = Span::new(input, 1, 7).unwrap();
739        let lines: Vec<_> = span.lines(()).collect();
740        let lines_span: Vec<_> = span.lines_span(()).map(|span| span.as_str()).collect();
741
742        assert_eq!(lines.len(), 2);
743        assert_eq!(lines[0], "abc\n".to_owned());
744        assert_eq!(lines[1], "def\n".to_owned());
745        assert_eq!(lines, lines_span) // Verify parity with lines_span()
746    }
747
748    #[test]
749    fn lines_eof() {
750        let input = "abc\ndef\nghi";
751        let span = Span::new(input, 5, 11).unwrap();
752        assert!(span.end_pos().at_end());
753        assert_eq!(span.end(), 11);
754        let lines: Vec<_> = span.lines(()).collect();
755        let lines_span: Vec<_> = span.lines_span(()).map(|span| span.as_str()).collect();
756
757        assert_eq!(lines.len(), 2);
758        assert_eq!(lines[0], "def\n".to_owned());
759        assert_eq!(lines[1], "ghi".to_owned());
760        assert_eq!(lines, lines_span) // Verify parity with lines_span()
761    }
762
763    #[test]
764    fn lines_span() {
765        let input = "abc\ndef\nghi";
766        let span = Span::new(input, 1, 7).unwrap();
767        let lines_span: Vec<_> = span.lines_span(()).collect();
768        let lines: Vec<_> = span.lines(()).collect();
769
770        assert_eq!(lines_span.len(), 2);
771        assert_eq!(lines_span[0], Span::new(input, 0, 4).unwrap());
772        assert_eq!(lines_span[1], Span::new(input, 4, 8).unwrap());
773        assert_eq!(
774            lines_span
775                .iter()
776                .map(|span| span.as_str())
777                .collect::<Vec<_>>(),
778            lines
779        );
780    }
781
782    #[test]
783    fn get_input_of_span() {
784        let input = "abc\ndef\nghi";
785        let span = Span::new(input, 1, 7).unwrap();
786
787        assert_eq!(span.get_input(), input);
788    }
789
790    #[test]
791    fn merge_contiguous() {
792        let input = "abc\ndef\nghi";
793        let span1 = Span::new(input, 1, 7).unwrap();
794        let span2 = Span::new(input, 7, 11).unwrap();
795        let merged = merge_spans(&span1, &span2).unwrap();
796
797        assert_eq!(merged, Span::new(input, 1, 11).unwrap());
798    }
799
800    #[test]
801    fn merge_overlapping() {
802        let input = "abc\ndef\nghi";
803        let span1 = Span::new(input, 1, 7).unwrap();
804        let span2 = Span::new(input, 5, 11).unwrap();
805        let merged = merge_spans(&span1, &span2).unwrap();
806
807        assert_eq!(merged, Span::new(input, 1, 11).unwrap());
808    }
809
810    #[test]
811    fn merge_non_contiguous() {
812        let input = "abc\ndef\nghi";
813        let span1 = Span::new(input, 1, 7).unwrap();
814        let span2 = Span::new(input, 8, 11).unwrap();
815        let merged = merge_spans(&span1, &span2);
816
817        assert!(merged.is_none());
818    }
819}