java_string/
iter.rs

1use std::fmt::{Debug, Display, Formatter, Write};
2use std::iter::{Chain, Copied, Filter, FlatMap, Flatten, FusedIterator, Map};
3use std::{option, slice};
4
5use crate::validations::{next_code_point, next_code_point_reverse};
6use crate::{CharEscapeIter, JavaCodePoint, JavaStr, JavaStrPattern};
7macro_rules! delegate {
8    (Iterator for $ty:ident $(<$($lt:lifetime),+>)? => $item:ty $(, DoubleEnded = $double_ended:ty)?) => {
9        impl$(<$($lt),+>)? Iterator for $ty$(<$($lt),+>)? {
10            type Item = $item;
11
12            #[inline]
13            fn next(&mut self) -> Option<Self::Item> {
14                self.inner.next()
15            }
16
17            #[inline]
18            fn size_hint(&self) -> (usize, Option<usize>) {
19                self.inner.size_hint()
20            }
21
22            #[inline]
23            fn count(self) -> usize {
24                self.inner.count()
25            }
26
27            #[inline]
28            fn last(self) -> Option<Self::Item> {
29                self.inner.last()
30            }
31
32            #[inline]
33            fn nth(&mut self, n: usize) -> Option<Self::Item> {
34                self.inner.nth(n)
35            }
36
37            #[inline]
38            fn all<F>(&mut self, f: F) -> bool
39            where
40                F: FnMut(Self::Item) -> bool,
41            {
42                self.inner.all(f)
43            }
44
45            #[inline]
46            fn any<F>(&mut self, f: F) -> bool
47            where
48                F: FnMut(Self::Item) -> bool,
49            {
50                self.inner.any(f)
51            }
52
53            #[inline]
54            fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
55            where
56                P: FnMut(&Self::Item) -> bool,
57            {
58                self.inner.find(predicate)
59            }
60
61            #[inline]
62            fn position<P>(&mut self, predicate: P) -> Option<usize>
63            where
64                P: FnMut(Self::Item) -> bool,
65            {
66                self.inner.position(predicate)
67            }
68
69            $(
70            #[inline]
71            fn rposition<P>(&mut self, predicate: P) -> Option<usize>
72            where
73                P: FnMut(Self::Item) -> bool,
74            {
75                let _test: $double_ended = ();
76                self.inner.rposition(predicate)
77            }
78            )?
79        }
80    };
81
82    (DoubleEndedIterator for $ty:ident $(<$($lt:lifetime),+>)?) => {
83        impl$(<$($lt),+>)? DoubleEndedIterator for $ty$(<$($lt),+>)? {
84            #[inline]
85            fn next_back(&mut self) -> Option<Self::Item> {
86                self.inner.next_back()
87            }
88
89            #[inline]
90            fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
91                self.inner.nth_back(n)
92            }
93
94            #[inline]
95            fn rfind<P>(&mut self, predicate: P) -> Option<Self::Item>
96            where
97                P: FnMut(&Self::Item) -> bool,
98            {
99                self.inner.rfind(predicate)
100            }
101        }
102    };
103
104    (ExactSizeIterator for $ty:ident $(<$($lt:lifetime),+>)?) => {
105        impl$(<$($lt),+>)? ExactSizeIterator for $ty$(<$($lt),+>)? {
106            #[inline]
107            fn len(&self) -> usize {
108                self.inner.len()
109            }
110        }
111    };
112
113    (FusedIterator for $ty:ident $(<$($lt:lifetime),+>)?) => {
114        impl$(<$($lt),+>)? FusedIterator for $ty$(<$($lt),+>)? {}
115    };
116
117    (Iterator, DoubleEndedIterator, ExactSizeIterator, FusedIterator for $ty:ident $(<$($lt:lifetime),+>)? => $item:ty) => {
118        delegate!(Iterator for $ty$(<$($lt),+>)? => $item, DoubleEnded = ());
119        delegate!(DoubleEndedIterator for $ty$(<$($lt),+>)?);
120        delegate!(ExactSizeIterator for $ty$(<$($lt),+>)?);
121        delegate!(FusedIterator for $ty$(<$($lt),+>)?);
122    };
123}
124
125#[must_use]
126#[derive(Clone, Debug)]
127pub struct Bytes<'a> {
128    pub(crate) inner: Copied<slice::Iter<'a, u8>>,
129}
130delegate!(Iterator, DoubleEndedIterator, ExactSizeIterator, FusedIterator for Bytes<'a> => u8);
131
132#[derive(Clone, Debug)]
133#[must_use]
134pub struct EscapeDebug<'a> {
135    #[allow(clippy::type_complexity)]
136    pub(crate) inner: Chain<
137        Flatten<option::IntoIter<CharEscapeIter>>,
138        FlatMap<Chars<'a>, CharEscapeIter, fn(JavaCodePoint) -> CharEscapeIter>,
139    >,
140}
141delegate!(Iterator for EscapeDebug<'a> => char);
142delegate!(FusedIterator for EscapeDebug<'a>);
143impl Display for EscapeDebug<'_> {
144    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
145        self.clone().try_for_each(|c| f.write_char(c))
146    }
147}
148
149#[derive(Clone, Debug)]
150#[must_use]
151pub struct EscapeDefault<'a> {
152    pub(crate) inner: FlatMap<Chars<'a>, CharEscapeIter, fn(JavaCodePoint) -> CharEscapeIter>,
153}
154delegate!(Iterator for EscapeDefault<'a> => char);
155delegate!(FusedIterator for EscapeDefault<'a>);
156impl Display for EscapeDefault<'_> {
157    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
158        self.clone().try_for_each(|c| f.write_char(c))
159    }
160}
161
162#[derive(Clone, Debug)]
163#[must_use]
164pub struct EscapeUnicode<'a> {
165    pub(crate) inner: FlatMap<Chars<'a>, CharEscapeIter, fn(JavaCodePoint) -> CharEscapeIter>,
166}
167delegate!(Iterator for EscapeUnicode<'a> => char);
168delegate!(FusedIterator for EscapeUnicode<'a>);
169impl Display for EscapeUnicode<'_> {
170    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
171        self.clone().try_for_each(|c| f.write_char(c))
172    }
173}
174
175#[derive(Clone, Debug)]
176#[must_use]
177pub struct Lines<'a> {
178    pub(crate) inner: Map<SplitInclusive<'a, char>, fn(&JavaStr) -> &JavaStr>,
179}
180delegate!(Iterator for Lines<'a> => &'a JavaStr);
181delegate!(DoubleEndedIterator for Lines<'a>);
182delegate!(FusedIterator for Lines<'a>);
183
184#[derive(Clone)]
185#[must_use]
186pub struct Chars<'a> {
187    pub(crate) inner: slice::Iter<'a, u8>,
188}
189
190impl Iterator for Chars<'_> {
191    type Item = JavaCodePoint;
192
193    #[inline]
194    fn next(&mut self) -> Option<Self::Item> {
195        // SAFETY: `JavaStr` invariant says `self.inner` is a semi-valid UTF-8 string
196        // and the resulting `ch` is a valid Unicode Scalar Value or surrogate
197        // code point.
198        unsafe { next_code_point(&mut self.inner).map(|ch| JavaCodePoint::from_u32_unchecked(ch)) }
199    }
200
201    // TODO: std has an optimized count impl
202
203    #[inline]
204    fn size_hint(&self) -> (usize, Option<usize>) {
205        let len = self.inner.len();
206        // `(len + 3)` can't overflow, because we know that the `slice::Iter`
207        // belongs to a slice in memory which has a maximum length of
208        // `isize::MAX` (that's well below `usize::MAX`).
209        (len.div_ceil(4), Some(len))
210    }
211
212    #[inline]
213    fn last(mut self) -> Option<JavaCodePoint> {
214        // No need to go through the entire string.
215        self.next_back()
216    }
217}
218
219impl Debug for Chars<'_> {
220    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
221        write!(f, "Chars(")?;
222        f.debug_list().entries(self.clone()).finish()?;
223        write!(f, ")")?;
224        Ok(())
225    }
226}
227
228impl DoubleEndedIterator for Chars<'_> {
229    #[inline]
230    fn next_back(&mut self) -> Option<Self::Item> {
231        // SAFETY: `JavaStr` invariant says `self.inner` is a semi-valid UTF-8 string
232        // and the resulting `ch` is a valid Unicode Scalar Value or surrogate
233        // code point.
234        unsafe {
235            next_code_point_reverse(&mut self.inner).map(|ch| JavaCodePoint::from_u32_unchecked(ch))
236        }
237    }
238}
239
240impl FusedIterator for Chars<'_> {}
241
242impl<'a> Chars<'a> {
243    #[inline]
244    #[must_use]
245    pub fn as_str(&self) -> &'a JavaStr {
246        // SAFETY: `Chars` is only made from a JavaStr, which guarantees the iter is
247        // semi-valid UTF-8.
248        unsafe { JavaStr::from_semi_utf8_unchecked(self.inner.as_slice()) }
249    }
250}
251
252#[derive(Clone, Debug)]
253#[must_use]
254pub struct CharIndices<'a> {
255    pub(crate) front_offset: usize,
256    pub(crate) inner: Chars<'a>,
257}
258
259impl Iterator for CharIndices<'_> {
260    type Item = (usize, JavaCodePoint);
261
262    #[inline]
263    fn next(&mut self) -> Option<(usize, JavaCodePoint)> {
264        let pre_len = self.inner.inner.len();
265        match self.inner.next() {
266            None => None,
267            Some(ch) => {
268                let index = self.front_offset;
269                let len = self.inner.inner.len();
270                self.front_offset += pre_len - len;
271                Some((index, ch))
272            }
273        }
274    }
275
276    #[inline]
277    fn count(self) -> usize {
278        self.inner.count()
279    }
280
281    #[inline]
282    fn size_hint(&self) -> (usize, Option<usize>) {
283        self.inner.size_hint()
284    }
285
286    #[inline]
287    fn last(mut self) -> Option<(usize, JavaCodePoint)> {
288        // No need to go through the entire string.
289        self.next_back()
290    }
291}
292
293impl DoubleEndedIterator for CharIndices<'_> {
294    #[inline]
295    fn next_back(&mut self) -> Option<(usize, JavaCodePoint)> {
296        self.inner.next_back().map(|ch| {
297            let index = self.front_offset + self.inner.inner.len();
298            (index, ch)
299        })
300    }
301}
302
303impl FusedIterator for CharIndices<'_> {}
304
305impl<'a> CharIndices<'a> {
306    #[inline]
307    #[must_use]
308    pub fn as_str(&self) -> &'a JavaStr {
309        self.inner.as_str()
310    }
311
312    #[inline]
313    #[must_use]
314    pub fn offset(&self) -> usize {
315        self.front_offset
316    }
317}
318
319#[must_use]
320#[derive(Debug, Clone)]
321pub struct Matches<'a, P> {
322    pub(crate) str: &'a JavaStr,
323    pub(crate) pat: P,
324}
325
326impl<'a, P> Iterator for Matches<'a, P>
327where
328    P: JavaStrPattern,
329{
330    type Item = &'a JavaStr;
331
332    #[inline]
333    fn next(&mut self) -> Option<Self::Item> {
334        if let Some((index, len)) = self.pat.find_in(self.str) {
335            // SAFETY: pattern returns valid indices
336            let ret = unsafe { self.str.get_unchecked(index..index + len) };
337            self.str = unsafe { self.str.get_unchecked(index + len..) };
338            Some(ret)
339        } else {
340            self.str = Default::default();
341            None
342        }
343    }
344}
345
346impl<P> DoubleEndedIterator for Matches<'_, P>
347where
348    P: JavaStrPattern,
349{
350    #[inline]
351    fn next_back(&mut self) -> Option<Self::Item> {
352        if let Some((index, len)) = self.pat.rfind_in(self.str) {
353            // SAFETY: pattern returns valid indices
354            let ret = unsafe { self.str.get_unchecked(index..index + len) };
355            self.str = unsafe { self.str.get_unchecked(..index) };
356            Some(ret)
357        } else {
358            self.str = Default::default();
359            None
360        }
361    }
362}
363
364#[must_use]
365#[derive(Clone, Debug)]
366pub struct RMatches<'a, P> {
367    pub(crate) inner: Matches<'a, P>,
368}
369
370impl<'a, P> Iterator for RMatches<'a, P>
371where
372    P: JavaStrPattern,
373{
374    type Item = &'a JavaStr;
375
376    #[inline]
377    fn next(&mut self) -> Option<Self::Item> {
378        self.inner.next_back()
379    }
380}
381
382impl<P> DoubleEndedIterator for RMatches<'_, P>
383where
384    P: JavaStrPattern,
385{
386    #[inline]
387    fn next_back(&mut self) -> Option<Self::Item> {
388        self.inner.next()
389    }
390}
391
392#[must_use]
393#[derive(Clone, Debug)]
394pub struct MatchIndices<'a, P> {
395    pub(crate) str: &'a JavaStr,
396    pub(crate) start: usize,
397    pub(crate) pat: P,
398}
399
400impl<'a, P> Iterator for MatchIndices<'a, P>
401where
402    P: JavaStrPattern,
403{
404    type Item = (usize, &'a JavaStr);
405
406    #[inline]
407    fn next(&mut self) -> Option<Self::Item> {
408        if let Some((index, len)) = self.pat.find_in(self.str) {
409            let full_index = self.start + index;
410            self.start = full_index + len;
411            // SAFETY: pattern returns valid indices
412            let ret = unsafe { self.str.get_unchecked(index..index + len) };
413            self.str = unsafe { self.str.get_unchecked(index + len..) };
414            Some((full_index, ret))
415        } else {
416            self.start += self.str.len();
417            self.str = Default::default();
418            None
419        }
420    }
421}
422
423impl<P> DoubleEndedIterator for MatchIndices<'_, P>
424where
425    P: JavaStrPattern,
426{
427    #[inline]
428    fn next_back(&mut self) -> Option<Self::Item> {
429        if let Some((index, len)) = self.pat.rfind_in(self.str) {
430            // SAFETY: pattern returns valid indices
431            let ret = unsafe { self.str.get_unchecked(index..index + len) };
432            self.str = unsafe { self.str.get_unchecked(..index) };
433            Some((self.start + index, ret))
434        } else {
435            self.str = Default::default();
436            None
437        }
438    }
439}
440
441#[derive(Clone, Debug)]
442pub struct RMatchIndices<'a, P> {
443    pub(crate) inner: MatchIndices<'a, P>,
444}
445
446impl<'a, P> Iterator for RMatchIndices<'a, P>
447where
448    P: JavaStrPattern,
449{
450    type Item = (usize, &'a JavaStr);
451
452    #[inline]
453    fn next(&mut self) -> Option<Self::Item> {
454        self.inner.next_back()
455    }
456}
457
458impl<P> DoubleEndedIterator for RMatchIndices<'_, P>
459where
460    P: JavaStrPattern,
461{
462    #[inline]
463    fn next_back(&mut self) -> Option<Self::Item> {
464        self.inner.next()
465    }
466}
467
468#[derive(Clone, Debug)]
469struct SplitHelper<'a, P> {
470    start: usize,
471    end: usize,
472    haystack: &'a JavaStr,
473    pat: P,
474    allow_trailing_empty: bool,
475    finished: bool,
476    had_empty_match: bool,
477}
478
479impl<'a, P> SplitHelper<'a, P>
480where
481    P: JavaStrPattern,
482{
483    #[inline]
484    fn new(haystack: &'a JavaStr, pat: P, allow_trailing_empty: bool) -> Self {
485        Self {
486            start: 0,
487            end: haystack.len(),
488            haystack,
489            pat,
490            allow_trailing_empty,
491            finished: false,
492            had_empty_match: false,
493        }
494    }
495
496    #[inline]
497    fn get_end(&mut self) -> Option<&'a JavaStr> {
498        if !self.finished {
499            self.finished = true;
500
501            if self.allow_trailing_empty || self.end - self.start > 0 {
502                // SAFETY: `self.start` and `self.end` always lie on unicode boundaries.
503                let string = unsafe { self.haystack.get_unchecked(self.start..self.end) };
504                return Some(string);
505            }
506        }
507
508        None
509    }
510
511    #[inline]
512    fn next_match(&mut self) -> Option<(usize, usize)> {
513        // SAFETY: `self.start` always lies on a unicode boundary.
514        let substr = unsafe { self.haystack.get_unchecked(self.start..) };
515
516        let result = if self.had_empty_match {
517            // if we had an empty match before, we are going to find the empty match again.
518            // don't do that, search from the next index along.
519
520            if substr.is_empty() {
521                None
522            } else {
523                // SAFETY: we can pop the string because we already checked if the string is
524                // empty above
525                let first_char_len = unsafe { substr.chars().next().unwrap_unchecked().len_utf8() };
526                let popped_str = unsafe { substr.get_unchecked(first_char_len..) };
527
528                self.pat
529                    .find_in(popped_str)
530                    .map(|(index, len)| (index + first_char_len + self.start, len))
531            }
532        } else {
533            self.pat
534                .find_in(substr)
535                .map(|(index, len)| (index + self.start, len))
536        };
537
538        self.had_empty_match = result.is_some_and(|(_, len)| len == 0);
539
540        result
541    }
542
543    #[inline]
544    fn next(&mut self) -> Option<&'a JavaStr> {
545        if self.finished {
546            return None;
547        }
548
549        match self.next_match() {
550            Some((index, len)) => unsafe {
551                // SAFETY: pattern guarantees valid indices
552                let elt = self.haystack.get_unchecked(self.start..index);
553                self.start = index + len;
554                Some(elt)
555            },
556            None => self.get_end(),
557        }
558    }
559
560    #[inline]
561    fn next_inclusive(&mut self) -> Option<&'a JavaStr> {
562        if self.finished {
563            return None;
564        }
565
566        match self.next_match() {
567            Some((index, len)) => unsafe {
568                // SAFETY: pattern guarantees valid indices
569                let elt = self.haystack.get_unchecked(self.start..index + len);
570                self.start = index + len;
571                Some(elt)
572            },
573            None => self.get_end(),
574        }
575    }
576
577    #[inline]
578    fn next_match_back(&mut self) -> Option<(usize, usize)> {
579        // SAFETY: `self.end` always lies on a unicode boundary.
580        let substr = unsafe { self.haystack.get_unchecked(..self.end) };
581
582        let result = if self.had_empty_match {
583            // if we had an empty match before, we are going to find the empty match again.
584            // don't do that, search from the next index along.
585
586            if substr.is_empty() {
587                None
588            } else {
589                // SAFETY: we can pop the string because we already checked if the string is
590                // empty above
591                let last_char_len =
592                    unsafe { substr.chars().next_back().unwrap_unchecked().len_utf8() };
593                let popped_str = unsafe { substr.get_unchecked(..substr.len() - last_char_len) };
594
595                self.pat.rfind_in(popped_str)
596            }
597        } else {
598            self.pat.rfind_in(substr)
599        };
600
601        self.had_empty_match = result.is_some_and(|(_, len)| len == 0);
602
603        result
604    }
605
606    #[inline]
607    fn next_back(&mut self) -> Option<&'a JavaStr> {
608        if self.finished {
609            return None;
610        }
611
612        if !self.allow_trailing_empty {
613            self.allow_trailing_empty = true;
614            match self.next_back() {
615                Some(elt) if !elt.is_empty() => return Some(elt),
616                _ => {
617                    if self.finished {
618                        return None;
619                    }
620                }
621            }
622        }
623
624        match self.next_match_back() {
625            Some((index, len)) => unsafe {
626                // SAFETY: pattern guarantees valid indices
627                let elt = self.haystack.get_unchecked(index + len..self.end);
628                self.end = index;
629                Some(elt)
630            },
631            None => unsafe {
632                // SAFETY: `self.start` and `self.end` always lie on unicode boundaries.
633                self.finished = true;
634                Some(self.haystack.get_unchecked(self.start..self.end))
635            },
636        }
637    }
638
639    #[inline]
640    fn next_back_inclusive(&mut self) -> Option<&'a JavaStr> {
641        if self.finished {
642            return None;
643        }
644
645        if !self.allow_trailing_empty {
646            self.allow_trailing_empty = true;
647            match self.next_back_inclusive() {
648                Some(elt) if !elt.is_empty() => return Some(elt),
649                _ => {
650                    if self.finished {
651                        return None;
652                    }
653                }
654            }
655        }
656
657        match self.next_match_back() {
658            Some((index, len)) => {
659                // SAFETY: pattern guarantees valid indices
660                let elt = unsafe { self.haystack.get_unchecked(index + len..self.end) };
661                self.end = index + len;
662                Some(elt)
663            }
664            None => {
665                self.finished = true;
666                // SAFETY: `self.start` and `self.end` always lie on unicode boundaries.
667                Some(unsafe { self.haystack.get_unchecked(self.start..self.end) })
668            }
669        }
670    }
671}
672
673#[derive(Clone, Debug)]
674pub struct Split<'a, P> {
675    inner: SplitHelper<'a, P>,
676}
677
678impl<'a, P> Split<'a, P>
679where
680    P: JavaStrPattern,
681{
682    #[inline]
683    pub(crate) fn new(haystack: &'a JavaStr, pat: P) -> Self {
684        Split {
685            inner: SplitHelper::new(haystack, pat, true),
686        }
687    }
688}
689
690impl<'a, P> Iterator for Split<'a, P>
691where
692    P: JavaStrPattern,
693{
694    type Item = &'a JavaStr;
695
696    #[inline]
697    fn next(&mut self) -> Option<Self::Item> {
698        self.inner.next()
699    }
700}
701
702impl<P> DoubleEndedIterator for Split<'_, P>
703where
704    P: JavaStrPattern,
705{
706    #[inline]
707    fn next_back(&mut self) -> Option<Self::Item> {
708        self.inner.next_back()
709    }
710}
711
712impl<P> FusedIterator for Split<'_, P> where P: JavaStrPattern {}
713
714#[derive(Clone, Debug)]
715pub struct RSplit<'a, P> {
716    inner: SplitHelper<'a, P>,
717}
718
719impl<'a, P> RSplit<'a, P>
720where
721    P: JavaStrPattern,
722{
723    #[inline]
724    pub(crate) fn new(haystack: &'a JavaStr, pat: P) -> Self {
725        RSplit {
726            inner: SplitHelper::new(haystack, pat, true),
727        }
728    }
729}
730
731impl<'a, P> Iterator for RSplit<'a, P>
732where
733    P: JavaStrPattern,
734{
735    type Item = &'a JavaStr;
736
737    #[inline]
738    fn next(&mut self) -> Option<Self::Item> {
739        self.inner.next_back()
740    }
741}
742
743impl<P> DoubleEndedIterator for RSplit<'_, P>
744where
745    P: JavaStrPattern,
746{
747    #[inline]
748    fn next_back(&mut self) -> Option<Self::Item> {
749        self.inner.next()
750    }
751}
752
753impl<P> FusedIterator for RSplit<'_, P> where P: JavaStrPattern {}
754
755#[derive(Clone, Debug)]
756pub struct SplitTerminator<'a, P> {
757    inner: SplitHelper<'a, P>,
758}
759
760impl<'a, P> SplitTerminator<'a, P>
761where
762    P: JavaStrPattern,
763{
764    #[inline]
765    pub(crate) fn new(haystack: &'a JavaStr, pat: P) -> Self {
766        SplitTerminator {
767            inner: SplitHelper::new(haystack, pat, false),
768        }
769    }
770}
771
772impl<'a, P> Iterator for SplitTerminator<'a, P>
773where
774    P: JavaStrPattern,
775{
776    type Item = &'a JavaStr;
777
778    #[inline]
779    fn next(&mut self) -> Option<Self::Item> {
780        self.inner.next()
781    }
782}
783
784impl<P> DoubleEndedIterator for SplitTerminator<'_, P>
785where
786    P: JavaStrPattern,
787{
788    #[inline]
789    fn next_back(&mut self) -> Option<Self::Item> {
790        self.inner.next_back()
791    }
792}
793
794impl<P> FusedIterator for SplitTerminator<'_, P> where P: JavaStrPattern {}
795
796#[derive(Clone, Debug)]
797pub struct RSplitTerminator<'a, P> {
798    inner: SplitHelper<'a, P>,
799}
800
801impl<'a, P> RSplitTerminator<'a, P>
802where
803    P: JavaStrPattern,
804{
805    #[inline]
806    pub(crate) fn new(haystack: &'a JavaStr, pat: P) -> Self {
807        RSplitTerminator {
808            inner: SplitHelper::new(haystack, pat, false),
809        }
810    }
811}
812
813impl<'a, P> Iterator for RSplitTerminator<'a, P>
814where
815    P: JavaStrPattern,
816{
817    type Item = &'a JavaStr;
818
819    #[inline]
820    fn next(&mut self) -> Option<Self::Item> {
821        self.inner.next_back()
822    }
823}
824
825impl<P> DoubleEndedIterator for RSplitTerminator<'_, P>
826where
827    P: JavaStrPattern,
828{
829    #[inline]
830    fn next_back(&mut self) -> Option<Self::Item> {
831        self.inner.next()
832    }
833}
834
835impl<P> FusedIterator for RSplitTerminator<'_, P> where P: JavaStrPattern {}
836
837#[derive(Clone, Debug)]
838pub struct SplitInclusive<'a, P> {
839    inner: SplitHelper<'a, P>,
840}
841
842impl<'a, P> SplitInclusive<'a, P>
843where
844    P: JavaStrPattern,
845{
846    #[inline]
847    pub(crate) fn new(haystack: &'a JavaStr, pat: P) -> Self {
848        SplitInclusive {
849            inner: SplitHelper::new(haystack, pat, false),
850        }
851    }
852}
853
854impl<'a, P> Iterator for SplitInclusive<'a, P>
855where
856    P: JavaStrPattern,
857{
858    type Item = &'a JavaStr;
859
860    #[inline]
861    fn next(&mut self) -> Option<Self::Item> {
862        self.inner.next_inclusive()
863    }
864}
865
866impl<P> DoubleEndedIterator for SplitInclusive<'_, P>
867where
868    P: JavaStrPattern,
869{
870    #[inline]
871    fn next_back(&mut self) -> Option<Self::Item> {
872        self.inner.next_back_inclusive()
873    }
874}
875
876impl<P> FusedIterator for SplitInclusive<'_, P> where P: JavaStrPattern {}
877
878#[derive(Clone, Debug)]
879pub struct SplitN<'a, P> {
880    inner: SplitHelper<'a, P>,
881    count: usize,
882}
883
884impl<'a, P> SplitN<'a, P>
885where
886    P: JavaStrPattern,
887{
888    #[inline]
889    pub(crate) fn new(haystack: &'a JavaStr, pat: P, count: usize) -> Self {
890        SplitN {
891            inner: SplitHelper::new(haystack, pat, true),
892            count,
893        }
894    }
895}
896
897impl<'a, P> Iterator for SplitN<'a, P>
898where
899    P: JavaStrPattern,
900{
901    type Item = &'a JavaStr;
902
903    #[inline]
904    fn next(&mut self) -> Option<Self::Item> {
905        match self.count {
906            0 => None,
907            1 => {
908                self.count = 0;
909                self.inner.get_end()
910            }
911            _ => {
912                self.count -= 1;
913                self.inner.next()
914            }
915        }
916    }
917}
918
919impl<P> FusedIterator for SplitN<'_, P> where P: JavaStrPattern {}
920
921#[derive(Clone, Debug)]
922pub struct RSplitN<'a, P> {
923    inner: SplitHelper<'a, P>,
924    count: usize,
925}
926
927impl<'a, P> RSplitN<'a, P>
928where
929    P: JavaStrPattern,
930{
931    #[inline]
932    pub(crate) fn new(haystack: &'a JavaStr, pat: P, count: usize) -> Self {
933        RSplitN {
934            inner: SplitHelper::new(haystack, pat, true),
935            count,
936        }
937    }
938}
939
940impl<'a, P> Iterator for RSplitN<'a, P>
941where
942    P: JavaStrPattern,
943{
944    type Item = &'a JavaStr;
945
946    #[inline]
947    fn next(&mut self) -> Option<Self::Item> {
948        match self.count {
949            0 => None,
950            1 => {
951                self.count = 0;
952                self.inner.get_end()
953            }
954            _ => {
955                self.count -= 1;
956                self.inner.next_back()
957            }
958        }
959    }
960}
961
962impl<P> FusedIterator for RSplitN<'_, P> where P: JavaStrPattern {}
963
964#[derive(Clone, Debug)]
965pub struct SplitAsciiWhitespace<'a> {
966    #[allow(clippy::type_complexity)]
967    pub(crate) inner: Map<
968        Filter<slice::Split<'a, u8, fn(&u8) -> bool>, fn(&&[u8]) -> bool>,
969        fn(&[u8]) -> &JavaStr,
970    >,
971}
972delegate!(Iterator for SplitAsciiWhitespace<'a> => &'a JavaStr);
973delegate!(DoubleEndedIterator for SplitAsciiWhitespace<'a>);
974delegate!(FusedIterator for SplitAsciiWhitespace<'a>);
975
976#[derive(Clone, Debug)]
977pub struct SplitWhitespace<'a> {
978    #[allow(clippy::type_complexity)]
979    pub(crate) inner: Filter<Split<'a, fn(JavaCodePoint) -> bool>, fn(&&JavaStr) -> bool>,
980}
981delegate!(Iterator for SplitWhitespace<'a> => &'a JavaStr);
982delegate!(DoubleEndedIterator for SplitWhitespace<'a>);
983delegate!(FusedIterator for SplitWhitespace<'a>);