detached_str/
slice.rs

1use std::fmt;
2use std::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
3
4/// A memory efficient string slice without a lifetime.
5///
6/// To get the content of the string slice, the original string
7/// must be still around.
8#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
9pub struct StrSlice {
10    pub(super) start: usize,
11    pub(super) end: usize,
12}
13
14impl StrSlice {
15    #[inline]
16    pub(crate) fn new(range: Range<usize>) -> Self {
17        StrSlice { start: range.start, end: range.end }
18    }
19
20    /// Get a reference to the str slice's start.
21    #[inline]
22    pub fn start(&self) -> usize {
23        self.start
24    }
25
26    /// Get a reference to the str slice's end.
27    #[inline]
28    pub fn end(&self) -> usize {
29        self.end
30    }
31
32    /// Returns the [`Range`] of this slice.
33    #[inline]
34    pub fn range(&self) -> Range<usize> {
35        self.start..self.end
36    }
37
38    /// Returns the length of this slice.
39    #[inline]
40    pub fn len(&self) -> usize {
41        self.end - self.start
42    }
43
44    /// Returns whether this slice is empty.
45    #[inline]
46    pub fn is_empty(&self) -> bool {
47        self.end == self.start
48    }
49
50    /// Joins this slice with another slice. The slices must be adjacent, i.e.
51    /// `self.end == other.start`.
52    ///
53    /// ## Panics
54    ///
55    /// Panics if the slices aren't adjacent.
56    #[inline]
57    pub fn join(self, other: Self) -> Self {
58        assert!(self.end == other.start);
59        StrSlice { start: self.start, end: other.end }
60    }
61
62    /// Joins this slice with another slice. It succeeds if the slices are
63    /// adjacent, i.e. `self.end == other.start`.
64    #[inline]
65    pub fn try_join(self, other: Self) -> Option<Self> {
66        if self.end == other.start {
67            Some(StrSlice { start: self.start, end: other.end })
68        } else {
69            None
70        }
71    }
72
73    /// Returns a subslice of this slice. This function accepts all kinds of
74    /// ranges.
75    pub fn get<T>(&self, index: T) -> StrSlice
76    where
77        Self: StrSliceIndex<T>,
78    {
79        self.index(index)
80    }
81
82    /// Returns the `&str` corresponding to this slice.
83    #[inline]
84    pub fn to_str(self, text: &str) -> &str {
85        &text[self.range()]
86    }
87
88    pub fn trim_end_matches<P>(self, pattern: P, text: &str) -> StrSlice
89    where
90        P: FnMut(char) -> bool,
91    {
92        let before = &text[self.range()];
93        let after = before.trim_end_matches(pattern);
94        let diff = before.len() - after.len();
95        StrSlice { start: self.start, end: self.end - diff }
96    }
97}
98
99impl fmt::Debug for StrSlice {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        write!(f, "StrSlice @ {}..{}", self.start, self.end)
102    }
103}
104
105/// Trait for indexing a [`StrSlice`] (using the [`StrSlice::get`] method)
106pub trait StrSliceIndex<T> {
107    fn index(&self, index: T) -> StrSlice;
108}
109
110impl StrSliceIndex<Range<usize>> for StrSlice {
111    #[inline]
112    fn index(&self, index: Range<usize>) -> StrSlice {
113        let (start, end) = (self.start + index.start, self.start + index.end);
114        if start > end || end > self.end {
115            panic!(
116                "Range {}..{} too big to index a StrSlice with length {}",
117                index.start,
118                index.end,
119                self.len(),
120            );
121        }
122        StrSlice::new(start..end)
123    }
124}
125
126impl StrSliceIndex<RangeInclusive<usize>> for StrSlice {
127    #[inline]
128    fn index(&self, index: RangeInclusive<usize>) -> StrSlice {
129        let (start, end) = (self.start + index.start(), self.start + index.end() + 1);
130        if start > end || end > self.end {
131            panic!(
132                "Range {}..={} too big to index a StrSlice with length {}",
133                index.start(),
134                index.end(),
135                self.len(),
136            );
137        }
138        StrSlice::new(start..end)
139    }
140}
141
142impl StrSliceIndex<RangeFrom<usize>> for StrSlice {
143    #[inline]
144    fn index(&self, index: RangeFrom<usize>) -> StrSlice {
145        let start = self.start + index.start;
146        if start > self.end {
147            panic!(
148                "Range {}.. too big to index a StrSlice with length {}",
149                index.start,
150                self.len(),
151            );
152        }
153        StrSlice::new(start..self.end)
154    }
155}
156
157impl StrSliceIndex<RangeTo<usize>> for StrSlice {
158    #[inline]
159    fn index(&self, index: RangeTo<usize>) -> StrSlice {
160        let end = self.start + index.end;
161        if end > self.end {
162            panic!("Range ..{} too big to index a StrSlice with length {}", index.end, self.len(),);
163        }
164        StrSlice::new(self.start..end)
165    }
166}
167
168impl StrSliceIndex<RangeToInclusive<usize>> for StrSlice {
169    #[inline]
170    fn index(&self, index: RangeToInclusive<usize>) -> StrSlice {
171        let end = self.start + index.end + 1;
172        if end > self.end {
173            panic!("Range ..{} too big to index a StrSlice with length {}", index.end, self.len(),);
174        }
175        StrSlice::new(self.start..end)
176    }
177}
178
179impl StrSliceIndex<RangeFull> for StrSlice {
180    #[inline]
181    fn index(&self, _: RangeFull) -> StrSlice {
182        *self
183    }
184}