konst/string/split_terminator_items.rs
1use crate::{
2 iter::{IntoIterKind, IsIteratorKind},
3 string::{self, str_from, str_up_to},
4};
5
6use konst_macro_rules::iterator_shared;
7
8/// Const equivalent of [`str::split_terminator`], which only takes a `&str` delimiter.
9///
10/// The same as [`split`](crate::string::split),
11/// except that, if the string after the last delimiter is empty, it is skipped.
12///
13/// # Version compatibility
14///
15/// This requires the `"rust_1_64"` feature.
16///
17/// # Example
18///
19/// ```rust
20/// use konst::string;
21/// use konst::iter::for_each;
22///
23/// const STRS: &[&str] = &{
24/// let mut arr = [""; 3];
25/// for_each!{(i, sub) in string::split_terminator("foo,bar,baz,", ","),enumerate() =>
26/// arr[i] = sub;
27/// }
28/// arr
29/// };
30///
31/// assert_eq!(STRS, ["foo", "bar", "baz"]);
32/// ```
33#[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_64")))]
34pub const fn split_terminator<'a, 'b>(this: &'a str, delim: &'b str) -> SplitTerminator<'a, 'b> {
35 SplitTerminator {
36 this,
37 state: if delim.is_empty() {
38 State::Empty(EmptyState::Start)
39 } else {
40 State::Normal { delim }
41 },
42 }
43}
44
45/// Const equivalent of [`str::rsplit_terminator`], which only takes a `&str` delimiter.
46///
47/// The same as [`rsplit`](crate::string::rsplit),
48/// except that, if the string before the first delimiter is empty, it is skipped.
49///
50/// # Version compatibility
51///
52/// This requires the `"rust_1_64"` feature.
53///
54/// # Example
55///
56/// ```rust
57/// use konst::string;
58/// use konst::iter::for_each;
59///
60/// const STRS: &[&str] = &{
61/// let mut arr = [""; 3];
62/// for_each!{(i, sub) in string::rsplit_terminator(":foo:bar:baz", ":"),enumerate() =>
63/// arr[i] = sub;
64/// }
65/// arr
66/// };
67///
68/// assert_eq!(STRS, ["baz", "bar", "foo"]);
69/// ```
70#[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_64")))]
71pub const fn rsplit_terminator<'a, 'b>(this: &'a str, delim: &'b str) -> RSplitTerminator<'a, 'b> {
72 let SplitTerminator { this, state } = split_terminator(this, delim);
73 RSplitTerminator { this, state }
74}
75
76#[derive(Copy, Clone)]
77enum State<'a> {
78 Normal { delim: &'a str },
79 Empty(EmptyState),
80}
81
82#[derive(Copy, Clone)]
83enum EmptyState {
84 Start,
85 Continue,
86}
87
88/// Const equivalent of `core::str::SplitTerminator<'a, &'b str>`
89///
90/// This is constructed with [`split_terminator`] like this:
91/// ```rust
92/// # let string = "";
93/// # let delim = "";
94/// # let _: konst::string::SplitTerminator<'_, '_> =
95/// konst::string::split_terminator(string, delim)
96/// # ;
97/// ```
98///
99/// # Version compatibility
100///
101/// This requires the `"rust_1_64"` feature.
102///
103#[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_64")))]
104pub struct SplitTerminator<'a, 'b> {
105 this: &'a str,
106 state: State<'b>,
107}
108impl IntoIterKind for SplitTerminator<'_, '_> {
109 type Kind = IsIteratorKind;
110}
111
112impl<'a, 'b> SplitTerminator<'a, 'b> {
113 iterator_shared! {
114 is_forward = true,
115 item = &'a str,
116 iter_forward = SplitTerminator<'a, 'b>,
117 next(self){
118 let Self {
119 this,
120 state,
121 } = self;
122
123 match state {
124 State::Empty(EmptyState::Start) => {
125 self.state = State::Empty(EmptyState::Continue);
126 Some(("", self))
127 }
128 _ if this.is_empty() => {
129 None
130 }
131 State::Normal{delim} => {
132 let (next, ret) = match string::find(this, delim, 0) {
133 Some(pos) => (pos + delim.len(), pos),
134 None => (this.len(), this.len()),
135 };
136 self.this = str_from(this, next);
137 Some((str_up_to(this, ret), self))
138 }
139 State::Empty(EmptyState::Continue) => {
140 let next_char = string::find_next_char_boundary(self.this.as_bytes(), 0);
141 let (next_char, rem) = string::split_at(self.this, next_char);
142 self.this = rem;
143 Some((next_char, self))
144 }
145 }
146 },
147 fields = {this, state},
148 }
149
150 /// Gets the remainder of the string.
151 ///
152 /// # Example
153 ///
154 /// ```rust
155 /// let iter = konst::string::split_terminator("foo,bar,baz,", ",");
156 /// assert_eq!(iter.remainder(), "foo,bar,baz,");
157 ///
158 /// let (elem, iter) = iter.next().unwrap();
159 /// assert_eq!(elem, "foo");
160 /// assert_eq!(iter.remainder(), "bar,baz,");
161 ///
162 /// let (elem, iter) = iter.next().unwrap();
163 /// assert_eq!(elem, "bar");
164 /// assert_eq!(iter.remainder(), "baz,");
165 ///
166 /// let (elem, iter) = iter.next().unwrap();
167 /// assert_eq!(elem, "baz");
168 /// assert_eq!(iter.remainder(), "");
169 ///
170 /// ```
171 pub const fn remainder(&self) -> &'a str {
172 self.this
173 }
174}
175
176/// Const equivalent of `core::str::RSplitTerminator<'a, &'b str>`
177///
178/// This is constructed with [`rsplit_terminator`] like this:
179/// ```rust
180/// # let string = "";
181/// # let delim = "";
182/// # let _: konst::string::RSplitTerminator<'_, '_> =
183/// konst::string::rsplit_terminator(string, delim)
184/// # ;
185/// ```
186///
187/// # Version compatibility
188///
189/// This requires the `"rust_1_64"` feature.
190///
191#[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_64")))]
192pub struct RSplitTerminator<'a, 'b> {
193 this: &'a str,
194 state: State<'b>,
195}
196impl IntoIterKind for RSplitTerminator<'_, '_> {
197 type Kind = IsIteratorKind;
198}
199
200impl<'a, 'b> RSplitTerminator<'a, 'b> {
201 iterator_shared! {
202 is_forward = true,
203 item = &'a str,
204 iter_forward = RSplitTerminator<'a, 'b>,
205 next(self){
206 let Self {
207 this,
208 state,
209 } = self;
210
211 match state {
212 State::Empty(EmptyState::Start) => {
213 self.state = State::Empty(EmptyState::Continue);
214 Some(("", self))
215 }
216 _ if this.is_empty() => {
217 None
218 }
219 State::Normal{delim} => {
220 let (next, ret) = match string::rfind(this, delim, this.len()) {
221 Some(pos) => (pos, pos + delim.len()),
222 None => (0, 0),
223 };
224 self.this = str_up_to(this, next);
225 Some((str_from(this, ret), self))
226 }
227 State::Empty(EmptyState::Continue) => {
228 let bytes = self.this.as_bytes();
229 let next_char = string::find_prev_char_boundary(bytes, bytes.len());
230 let (rem, next_char) = string::split_at(self.this, next_char);
231 self.this = rem;
232 Some((next_char, self))
233 }
234 }
235 },
236 fields = {this, state},
237 }
238
239 /// Gets the remainder of the string.
240 ///
241 /// # Example
242 ///
243 /// ```rust
244 /// let iter = konst::string::rsplit_terminator("=foo=bar=baz", "=");
245 /// assert_eq!(iter.remainder(), "=foo=bar=baz");
246 ///
247 /// let (elem, iter) = iter.next().unwrap();
248 /// assert_eq!(elem, "baz");
249 /// assert_eq!(iter.remainder(), "=foo=bar");
250 ///
251 /// let (elem, iter) = iter.next().unwrap();
252 /// assert_eq!(elem, "bar");
253 /// assert_eq!(iter.remainder(), "=foo");
254 ///
255 /// let (elem, iter) = iter.next().unwrap();
256 /// assert_eq!(elem, "foo");
257 /// assert_eq!(iter.remainder(), "");
258 ///
259 /// ```
260 pub const fn remainder(&self) -> &'a str {
261 self.this
262 }
263}