1use core::{ops::Range, str::Lines};
2
3mod sealed {
4 pub trait Sealed {}
5}
6
7pub trait StrExt: sealed::Sealed {
8 #[doc(hidden)]
9 fn __not_object_safe(&self) -> (&Self, &Self) {
10 (self, self)
11 }
12 fn line_indices(&self) -> LineIndices<'_>;
13}
14
15impl sealed::Sealed for str {}
16
17impl StrExt for str {
18 #[inline]
19 fn line_indices(&self) -> LineIndices<'_> {
20 LineIndices {
21 start_addr: self as *const str as *const () as usize,
22 inner: self.lines(),
23 }
24 }
25}
26
27#[cfg(feature = "alloc")]
28impl sealed::Sealed for alloc::string::String {}
29
30#[cfg(feature = "alloc")]
31impl StrExt for alloc::string::String {
32 #[inline]
33 fn line_indices(&self) -> LineIndices<'_> {
34 self.as_str().line_indices()
35 }
36}
37
38pub struct LineIndices<'a> {
39 start_addr: usize,
40 inner: Lines<'a>,
41}
42
43impl<'a> Iterator for LineIndices<'a> {
44 type Item = (Range<usize>, &'a str);
45
46 #[inline]
47 fn next(&mut self) -> Option<Self::Item> {
48 let slice = self.inner.next()?;
49 let offset = (slice as *const str as *const () as usize) - self.start_addr;
50 Some((offset..offset + slice.len(), slice))
51 }
52}