1use std::fmt;
2use std::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
3
4#[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 #[inline]
22 pub fn start(&self) -> usize {
23 self.start
24 }
25
26 #[inline]
28 pub fn end(&self) -> usize {
29 self.end
30 }
31
32 #[inline]
34 pub fn range(&self) -> Range<usize> {
35 self.start..self.end
36 }
37
38 #[inline]
40 pub fn len(&self) -> usize {
41 self.end - self.start
42 }
43
44 #[inline]
46 pub fn is_empty(&self) -> bool {
47 self.end == self.start
48 }
49
50 #[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 #[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 pub fn get<T>(&self, index: T) -> StrSlice
76 where
77 Self: StrSliceIndex<T>,
78 {
79 self.index(index)
80 }
81
82 #[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
105pub 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}