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}