sub_strs/sub_str_iter.rs
1/*
2==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--
3
4sub-strs
5
6Copyright (C) 2019-2024 Anonymous
7
8There are several releases over multiple years,
9they are listed as ranges, such as: "2019-2024".
10
11This program is free software: you can redistribute it and/or modify
12it under the terms of the GNU Lesser General Public License as published by
13the Free Software Foundation, either version 3 of the License, or
14(at your option) any later version.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19GNU Lesser General Public License for more details.
20
21You should have received a copy of the GNU Lesser General Public License
22along with this program. If not, see <https://www.gnu.org/licenses/>.
23
24::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
25*/
26
27//! # `SubStrIter`
28
29use {
30 alloc::string::String,
31 core::ops::{Deref, Range},
32};
33
34/// # A sub string
35///
36/// This struct is used by [`SubStrIter`][::SubStrIter], which can be made via [`sub_strs()`][::sub_strs()].
37///
38/// You can dereference this struct to `&str`. It can also give you start and end indexes of the original string.
39///
40/// [::SubStrIter]: struct.SubStrIter.html
41/// [::sub_strs()]: fn.sub_strs.html
42#[derive(Debug)]
43pub struct SubStr<'a> {
44 target: &'a str,
45
46 start: usize,
47 end: usize,
48
49 inner: &'a str,
50 inner_start: usize,
51 inner_end: usize,
52}
53
54impl<'a> SubStr<'a> {
55
56 /// # Start index within original string (inclusive)
57 pub fn start(&self) -> usize {
58 self.start
59 }
60
61 /// # End index within original string (exclusive)
62 pub fn end(&self) -> usize {
63 self.end
64 }
65
66 /// # Range within original string
67 pub fn range(&self) -> Range<usize> {
68 self.start..self.end
69 }
70
71 /// # Inner string between start and end phrases
72 ///
73 /// # Examples
74 ///
75 /// ```
76 /// use core::str::FromStr;
77 ///
78 /// let sample = "some_id: 99,";
79 /// let some_id = sub_strs::sub_strs(sample, "some_id:", ",").next().unwrap();
80 /// assert_eq!(u8::from_str(some_id.inner().trim()).unwrap(), 99);
81 /// ```
82 pub fn inner(&self) -> &'a str {
83 self.inner
84 }
85
86 /// # Start index of the inner string within original string (inclusive)
87 pub fn inner_start(&self) -> usize {
88 self.inner_start
89 }
90
91 /// # End index of the inner string within original string (exclusive)
92 pub fn inner_end(&self) -> usize {
93 self.inner_end
94 }
95
96 /// # Range of the inner string within original string
97 pub fn inner_range(&self) -> Range<usize> {
98 self.inner_start..self.inner_end
99 }
100
101}
102
103impl Deref for SubStr<'_> {
104
105 type Target = str;
106
107 fn deref(&self) -> &Self::Target {
108 self.target
109 }
110
111}
112
113impl PartialEq<str> for SubStr<'_> {
114
115 fn eq(&self, other: &str) -> bool {
116 self.target == other
117 }
118
119}
120
121impl PartialEq<SubStr<'_>> for &str {
122
123 fn eq(&self, other: &SubStr) -> bool {
124 self == &other.target
125 }
126
127}
128
129impl PartialEq<String> for SubStr<'_> {
130
131 fn eq(&self, other: &String) -> bool {
132 self.target == other
133 }
134
135}
136
137impl PartialEq<SubStr<'_>> for String {
138
139 fn eq(&self, other: &SubStr) -> bool {
140 self == other.target
141 }
142
143}
144
145/// # An iterator of [`SubStr`][::SubStr]
146///
147/// You can make an instance of this struct by [`sub_strs()`][::sub_strs()].
148///
149/// [::SubStr]: struct.SubStr.html
150/// [::sub_strs()]: fn.sub_strs.html
151#[derive(Debug)]
152pub struct SubStrIter<'a, S, T> where S: AsRef<str>, T: AsRef<str> {
153 src: &'a str,
154 src_index: usize,
155 start: S,
156 end: T,
157}
158
159impl<'a, S, T> SubStrIter<'a, S, T> where S: AsRef<str>, T: AsRef<str> {
160
161 /// # Makes new instance
162 fn new(source: &'a str, start: S, end: T) -> Self {
163 Self {
164 src: source,
165 src_index: 0,
166 start,
167 end,
168 }
169 }
170
171}
172
173impl<'a, S, T> Iterator for SubStrIter<'a, S, T> where S: AsRef<str>, T: AsRef<str> {
174
175 type Item = SubStr<'a>;
176
177 fn next(&mut self) -> Option<Self::Item> {
178 let start = self.start.as_ref();
179 let start_len = start.len();
180
181 let src = &self.src[self.src_index..];
182 if let Some(mut start_idx) = src.find(start) {
183 let end = self.end.as_ref();
184 let delta = start_idx + start_len;
185 if let Some(end_idx) = src[delta..].find(end) {
186 if start_len > 0 && end_idx > 0 {
187 if let Some(next_char_bytes) = src[start_idx..].chars().next().map(|c| c.len_utf8()) {
188 if let Some(next_start_idx) = src[start_idx + next_char_bytes .. delta + end_idx].rfind(start) {
189 start_idx += next_char_bytes + next_start_idx;
190 }
191 }
192 }
193
194 let end_len = end.len();
195 let end_idx = delta + end_idx + end_len;
196 let sub_start = self.src_index + start_idx;
197 self.src_index += end_idx;
198
199 let target = &src[start_idx..end_idx];
200 let inner = &target[start_len .. target.len() - end_len];
201
202 return {
203 let start_idx = sub_start;
204 let end_idx = self.src_index;
205 let inner_start = start_idx + start.len();
206 let inner_end = end_idx - end.len();
207 Some(SubStr { target, start: start_idx, end: end_idx, inner, inner_start, inner_end })
208 };
209 }
210 }
211
212 None
213 }
214
215}
216
217/// # Finds sub strings
218///
219/// ## Examples
220///
221/// ```
222/// use core::str::FromStr;
223///
224/// let sample = "test(some(test(0) test(1)---test(2) some test(3)";
225/// let start = "test(";
226/// let end = ")";
227/// for (i, s) in sub_strs::sub_strs(sample, start, end).enumerate() {
228/// let expected = format!("{}{}{}", start, i, end);
229/// assert_eq!(s, expected);
230/// assert_eq!(&sample[s.start()..s.end()], expected);
231/// assert_eq!(usize::from_str(s.inner()).unwrap(), i);
232/// }
233/// ```
234pub fn sub_strs<'a, S, T>(source: &'a str, start: S, end: T) -> SubStrIter<'a, S, T> where S: AsRef<str>, T: AsRef<str> {
235 SubStrIter::new(source, start, end)
236}