1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
use core::{ops::Range, str::Lines};

mod sealed {
    pub trait Sealed {}
}

pub trait StrExt: sealed::Sealed {
    #[doc(hidden)]
    fn not_object_safe(&self) -> (&Self, &Self) {
        (self, self)
    }
    fn line_indices(&self) -> LineIndices<'_>;
}

impl sealed::Sealed for str {}

impl StrExt for str {
    #[inline]
    fn line_indices(&self) -> LineIndices<'_> {
        LineIndices {
            start_addr: self as *const str as *const () as usize,
            inner: self.lines(),
        }
    }
}

#[cfg(feature = "alloc")]
impl sealed::Sealed for alloc::string::String {}

#[cfg(feature = "alloc")]
impl StrExt for alloc::string::String {
    #[inline]
    fn line_indices(&self) -> LineIndices<'_> {
        self.as_str().line_indices()
    }
}

pub struct LineIndices<'a> {
    start_addr: usize,
    inner: Lines<'a>,
}

impl<'a> Iterator for LineIndices<'a> {
    type Item = (Range<usize>, &'a str);

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        let slice = self.inner.next()?;
        let offset = (slice as *const str as *const () as usize) - self.start_addr;
        Some((offset..offset + slice.len(), slice))
    }
}