g_string/iterator.rs
1//! Iterator implementations for [`GString`].
2//!
3//! You implemented 3 major iterator categories for [`GString`].
4//!
5//! ---
6//!
7//! # 1. Borrowed Iteration (`&GString`)
8//!
9//! Uses std’s native [`core::str::Chars`] iterator.
10//!
11//! ```ignore
12//! for ch in &gstring {
13//! println!("{ch}");
14//! }
15//! ```
16//!
17//! Iterator type:
18//!
19//! ```ignore
20//! core::str::Chars<'a>
21//! ```
22//!
23//! Characteristics:
24//!
25//! - non-owning
26//! - zero allocation
27//! - lightweight
28//! - identical to `&str`
29//! - read-only iteration
30//!
31//! Implementation:
32//!
33//! ```ignore
34//! impl IntoIterator for &GString
35//! ```
36//!
37//! ---
38//!
39//! # 2. Mutable Borrowed Iteration (`&mut GString`)
40//!
41//! Also uses [`core::str::Chars`].
42//!
43//! ```ignore
44//! for ch in &mut gstring {
45//! println!("{ch}");
46//! }
47//! ```
48//!
49//! Characteristics:
50//!
51//! - mutable borrow of container
52//! - iteration still yields immutable [`char`] values
53//! - prevents simultaneous mutation during iteration
54//!
55//! Implementation:
56//!
57//! ```ignore
58//! impl IntoIterator for &mut GString
59//! ```
60//!
61//! ---
62//!
63//! # 3. Owned Iteration (`GString`)
64//!
65//! Consumes the string and iterates characters using the custom
66//! [`IntoChars`] iterator.
67//!
68//! ```ignore
69//! for ch in gstring {
70//! println!("{ch}");
71//! }
72//! ```
73//!
74//! Iterator type:
75//!
76//! ```ignore
77//! IntoChars
78//! ```
79//!
80//! Characteristics:
81//!
82//! - owning iterator
83//! - consumes [`GString`]
84//! - supports forward + reverse iteration
85//! - ASCII fast path
86//! - UTF-8 aware
87//! - double-ended
88//!
89//! Implementation:
90//!
91//! ```ignore
92//! impl IntoIterator for GString
93//! ```
94//!
95//! ---
96//!
97//! # 4. Forward Iterator (`Iterator`)
98//!
99//! Supports:
100//!
101//! ```ignore
102//! iter.next()
103//! ```
104//!
105//! Example:
106//!
107//! ```ignore
108//! let mut iter = gstring.into_iter();
109//!
110//! assert_eq!(iter.next(), Some('a'));
111//! assert_eq!(iter.next(), Some('b'));
112//! ```
113//!
114//! Features:
115//!
116//! - UTF-8 decoding
117//! - ASCII optimization
118//! - maintains `front/back` cursor state
119//! - custom [`Iterator::size_hint`]
120//!
121//! Implementation:
122//!
123//! ```ignore
124//! impl Iterator for IntoChars
125//! ```
126//!
127//! ---
128//!
129//! # 5. Reverse Iterator (`DoubleEndedIterator`)
130//!
131//! Supports reverse iteration:
132//!
133//! ```ignore
134//! gstring.into_iter().rev()
135//! ```
136//!
137//! Example:
138//!
139//! ```ignore
140//! let mut iter = gstring.into_iter();
141//!
142//! assert_eq!(iter.next_back(), Some('z'));
143//! assert_eq!(iter.next_back(), Some('y'));
144//! ```
145//!
146//! Features:
147//!
148//! - reverse UTF-8 traversal
149//! - UTF-8 character-boundary walking
150//! - amortized O(n)
151//! - ASCII fast path
152//!
153//! Implementation:
154//!
155//! ```ignore
156//! impl DoubleEndedIterator for IntoChars
157//! ```
158//!
159//! ---
160//!
161//! # 6. Fused Iterator (`FusedIterator`)
162//!
163//! Guarantees exhaustion permanence.
164//!
165//! Example:
166//!
167//! ```ignore
168//! let mut iter = gstring.into_iter();
169//!
170//! while iter.next().is_some() {}
171//!
172//! assert_eq!(iter.next(), None);
173//! assert_eq!(iter.next(), None);
174//! ```
175//!
176//! Meaning:
177//!
178//! ```text
179//! once None, always None
180//! ```
181//!
182//! Implementation:
183//!
184//! ```ignore
185//! impl FusedIterator for IntoChars
186//! ```
187//!
188//! ---
189//!
190//! # 7. Sized Iterator Hint (`size_hint`)
191//!
192//! Provides iteration bounds information.
193//!
194//! Example:
195//!
196//! ```ignore
197//! let iter = gstring.into_iter();
198//!
199//! let (min, max) = iter.size_hint();
200//! ```
201//!
202//! ASCII case:
203//!
204//! ```text
205//! exact remaining chars known
206//! ```
207//!
208//! UTF-8 case:
209//!
210//! ```text
211//! upper bound only
212//! ```
213//!
214//! ---
215//!
216//! # Overall Design
217//!
218//! Your iterator system now has:
219//!
220//! - borrowed iteration
221//! - mutable borrowed iteration
222//! - owned iteration
223//! - forward iteration
224//! - reverse iteration
225//! - fused semantics
226//! - UTF-8 correctness
227//! - ASCII optimization
228//! - allocation-aware sizing hints
229//!
230//! This design is comparable to a proper std-quality custom string iterator
231//! implementation.
232
233use crate::{GString, Validator};
234
235/// Iterates over the characters of a borrowed [`GString`].
236///
237/// This behaves the same as iterating over `&str`.
238///
239/// # Examples
240///
241/// ```ignore
242/// for ch in &gstring {
243/// // ch: char
244/// }
245/// ```
246impl<'a, V, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> IntoIterator
247 for &'a GString<V, MIN, MAX, ASCII_ONLY>
248where
249 V: Validator,
250{
251 type Item = char;
252 type IntoIter = core::str::Chars<'a>;
253
254 #[inline]
255 fn into_iter(self) -> Self::IntoIter {
256 self.chars()
257 }
258}
259
260/// Iterates over the characters of a mutably borrowed [`GString`].
261///
262/// Even though the string is mutably borrowed, iteration yields immutable
263/// [`char`] values.
264///
265/// # Examples
266///
267/// ```ignore
268/// for ch in &mut gstring {
269/// // ch: char
270/// }
271/// ```
272impl<'a, V, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> IntoIterator
273 for &'a mut GString<V, MIN, MAX, ASCII_ONLY>
274where
275 V: Validator,
276{
277 type Item = char;
278 type IntoIter = core::str::Chars<'a>;
279
280 #[inline]
281 fn into_iter(self) -> Self::IntoIter {
282 self.chars()
283 }
284}
285
286/// An owning iterator over the characters of a [`GString`].
287///
288/// This iterator is created by [`IntoIterator`] for owned [`GString`].
289///
290/// # Examples
291///
292/// ```ignore
293/// let iter = gstring.into_iter();
294/// ```
295pub struct IntoChars<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> {
296 inner: GString<V, MIN, MAX, ASCII_ONLY>,
297 front: usize,
298 back: usize,
299}
300
301impl<V, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> Iterator
302 for IntoChars<V, MIN, MAX, ASCII_ONLY>
303where
304 V: Validator,
305{
306 type Item = char;
307
308 #[inline]
309 fn next(&mut self) -> Option<Self::Item> {
310 if self.front >= self.back {
311 return None;
312 }
313
314 if ASCII_ONLY {
315 let byte = self.inner.buf[self.front];
316 self.front += 1;
317 return Some(byte as char);
318 }
319
320 let s = self.inner.as_str();
321 let ch = s[self.front..self.back].chars().next()?;
322 self.front += ch.len_utf8();
323 Some(ch)
324 }
325
326 #[inline]
327 fn size_hint(&self) -> (usize, Option<usize>) {
328 let remaining = self.back - self.front;
329
330 if ASCII_ONLY {
331 return (remaining, Some(remaining));
332 }
333
334 let min = remaining.div_ceil(4);
335
336 (min, Some(remaining))
337 }
338}
339
340/// Iterates over the characters of an owned [`GString`].
341///
342/// This consumes the string.
343///
344/// # Examples
345///
346/// ```ignore
347/// for ch in gstring {
348/// // ch: char
349/// }
350/// ```
351impl<V, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> IntoIterator
352 for GString<V, MIN, MAX, ASCII_ONLY>
353where
354 V: Validator,
355{
356 type Item = char;
357 type IntoIter = IntoChars<V, MIN, MAX, ASCII_ONLY>;
358
359 #[inline]
360 fn into_iter(self) -> Self::IntoIter {
361 let len = self.len;
362
363 IntoChars {
364 inner: self,
365 front: 0,
366 back: len,
367 }
368 }
369}
370
371/// Enables reverse iteration
372///
373/// # Examples
374/// ```ignored
375/// gstring.into_iter().rev()
376/// ```
377impl<V, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> DoubleEndedIterator
378 for IntoChars<V, MIN, MAX, ASCII_ONLY>
379where
380 V: Validator,
381{
382 #[inline]
383 fn next_back(&mut self) -> Option<char> {
384 if self.front >= self.back {
385 return None;
386 }
387
388 // ASCII fast path
389 if ASCII_ONLY {
390 self.back -= 1;
391 return Some(self.inner.buf[self.back] as char);
392 }
393
394 let s = self.inner.as_str();
395
396 // Start from the final byte
397 let mut idx = self.back - 1;
398
399 // Walk backwards until UTF-8 char boundary
400 while idx > self.front && !s.is_char_boundary(idx) {
401 idx -= 1;
402 }
403
404 // Extract the char slice
405 let ch = s[idx..self.back].chars().next()?;
406
407 // Move back pointer
408 self.back = idx;
409
410 Some(ch)
411 }
412}
413
414impl<V, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> core::iter::FusedIterator
415 for IntoChars<V, MIN, MAX, ASCII_ONLY>
416where
417 V: Validator,
418{
419}