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.div_ceil(4), Some(len))
207    }
208
209    #[inline]
210    fn last(mut self) -> Option<JavaCodePoint> {
211        // No need to go through the entire string.
212        self.next_back()
213    }
214}
215
216impl Debug for Chars<'_> {
217    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
218        write!(f, "Chars(")?;
219        f.debug_list().entries(self.clone()).finish()?;
220        write!(f, ")")?;
221        Ok(())
222    }
223}
224
225impl DoubleEndedIterator for Chars<'_> {
226    #[inline]
227    fn next_back(&mut self) -> Option<Self::Item> {
228        // SAFETY: `JavaStr` invariant says `self.inner` is a semi-valid UTF-8 string
229        // and the resulting `ch` is a valid Unicode Scalar Value or surrogate
230        // code point.
231        unsafe {
232            next_code_point_reverse(&mut self.inner).map(|ch| JavaCodePoint::from_u32_unchecked(ch))
233        }
234    }
235}
236
237impl FusedIterator for Chars<'_> {}
238
239impl<'a> Chars<'a> {
240    #[inline]
241    #[must_use]
242    pub fn as_str(&self) -> &'a JavaStr {
243        // SAFETY: `Chars` is only made from a JavaStr, which guarantees the iter is
244        // semi-valid UTF-8.
245        unsafe { JavaStr::from_semi_utf8_unchecked(self.inner.as_slice()) }
246    }
247}
248
249#[derive(Clone, Debug)]
250#[must_use]
251pub struct CharIndices<'a> {
252    pub(crate) front_offset: usize,
253    pub(crate) inner: Chars<'a>,
254}
255
256impl Iterator for CharIndices<'_> {
257    type Item = (usize, JavaCodePoint);
258
259    #[inline]
260    fn next(&mut self) -> Option<(usize, JavaCodePoint)> {
261        let pre_len = self.inner.inner.len();
262        match self.inner.next() {
263            None => None,
264            Some(ch) => {
265                let index = self.front_offset;
266                let len = self.inner.inner.len();
267                self.front_offset += pre_len - len;
268                Some((index, ch))
269            }
270        }
271    }
272
273    #[inline]
274    fn count(self) -> usize {
275        self.inner.count()
276    }
277
278    #[inline]
279    fn size_hint(&self) -> (usize, Option<usize>) {
280        self.inner.size_hint()
281    }
282
283    #[inline]
284    fn last(mut self) -> Option<(usize, JavaCodePoint)> {
285        // No need to go through the entire string.
286        self.next_back()
287    }
288}
289
290impl DoubleEndedIterator for CharIndices<'_> {
291    #[inline]
292    fn next_back(&mut self) -> Option<(usize, JavaCodePoint)> {
293        self.inner.next_back().map(|ch| {
294            let index = self.front_offset + self.inner.inner.len();
295            (index, ch)
296        })
297    }
298}
299
300impl FusedIterator for CharIndices<'_> {}
301
302impl<'a> CharIndices<'a> {
303    #[inline]
304    #[must_use]
305    pub fn as_str(&self) -> &'a JavaStr {
306        self.inner.as_str()
307    }
308}
309
310#[must_use]
311#[derive(Debug, Clone)]
312pub struct Matches<'a, P> {
313    pub(crate) str: &'a JavaStr,
314    pub(crate) pat: P,
315}
316
317impl<'a, P> Iterator for Matches<'a, P>
318where
319    P: JavaStrPattern,
320{
321    type Item = &'a JavaStr;
322
323    #[inline]
324    fn next(&mut self) -> Option<Self::Item> {
325        if let Some((index, len)) = self.pat.find_in(self.str) {
326            // SAFETY: pattern returns valid indices
327            let ret = unsafe { self.str.get_unchecked(index..index + len) };
328            self.str = unsafe { self.str.get_unchecked(index + len..) };
329            Some(ret)
330        } else {
331            self.str = Default::default();
332            None
333        }
334    }
335}
336
337impl<P> DoubleEndedIterator for Matches<'_, P>
338where
339    P: JavaStrPattern,
340{
341    #[inline]
342    fn next_back(&mut self) -> Option<Self::Item> {
343        if let Some((index, len)) = self.pat.rfind_in(self.str) {
344            // SAFETY: pattern returns valid indices
345            let ret = unsafe { self.str.get_unchecked(index..index + len) };
346            self.str = unsafe { self.str.get_unchecked(..index) };
347            Some(ret)
348        } else {
349            self.str = Default::default();
350            None
351        }
352    }
353}
354
355#[must_use]
356#[derive(Clone, Debug)]
357pub struct RMatches<'a, P> {
358    pub(crate) inner: Matches<'a, P>,
359}
360
361impl<'a, P> Iterator for RMatches<'a, P>
362where
363    P: JavaStrPattern,
364{
365    type Item = &'a JavaStr;
366
367    #[inline]
368    fn next(&mut self) -> Option<Self::Item> {
369        self.inner.next_back()
370    }
371}
372
373impl<P> DoubleEndedIterator for RMatches<'_, P>
374where
375    P: JavaStrPattern,
376{
377    #[inline]
378    fn next_back(&mut self) -> Option<Self::Item> {
379        self.inner.next()
380    }
381}
382
383#[must_use]
384#[derive(Clone, Debug)]
385pub struct MatchIndices<'a, P> {
386    pub(crate) str: &'a JavaStr,
387    pub(crate) start: usize,
388    pub(crate) pat: P,
389}
390
391impl<'a, P> Iterator for MatchIndices<'a, P>
392where
393    P: JavaStrPattern,
394{
395    type Item = (usize, &'a JavaStr);
396
397    #[inline]
398    fn next(&mut self) -> Option<Self::Item> {
399        if let Some((index, len)) = self.pat.find_in(self.str) {
400            let full_index = self.start + index;
401            self.start = full_index + len;
402            // SAFETY: pattern returns valid indices
403            let ret = unsafe { self.str.get_unchecked(index..index + len) };
404            self.str = unsafe { self.str.get_unchecked(index + len..) };
405            Some((full_index, ret))
406        } else {
407            self.start += self.str.len();
408            self.str = Default::default();
409            None
410        }
411    }
412}
413
414impl<P> DoubleEndedIterator for MatchIndices<'_, P>
415where
416    P: JavaStrPattern,
417{
418    #[inline]
419    fn next_back(&mut self) -> Option<Self::Item> {
420        if let Some((index, len)) = self.pat.rfind_in(self.str) {
421            // SAFETY: pattern returns valid indices
422            let ret = unsafe { self.str.get_unchecked(index..index + len) };
423            self.str = unsafe { self.str.get_unchecked(..index) };
424            Some((self.start + index, ret))
425        } else {
426            self.str = Default::default();
427            None
428        }
429    }
430}
431
432#[derive(Clone, Debug)]
433pub struct RMatchIndices<'a, P> {
434    pub(crate) inner: MatchIndices<'a, P>,
435}
436
437impl<'a, P> Iterator for RMatchIndices<'a, P>
438where
439    P: JavaStrPattern,
440{
441    type Item = (usize, &'a JavaStr);
442
443    #[inline]
444    fn next(&mut self) -> Option<Self::Item> {
445        self.inner.next_back()
446    }
447}
448
449impl<P> DoubleEndedIterator for RMatchIndices<'_, P>
450where
451    P: JavaStrPattern,
452{
453    #[inline]
454    fn next_back(&mut self) -> Option<Self::Item> {
455        self.inner.next()
456    }
457}
458
459#[derive(Clone, Debug)]
460struct SplitHelper<'a, P> {
461    start: usize,
462    end: usize,
463    haystack: &'a JavaStr,
464    pat: P,
465    allow_trailing_empty: bool,
466    finished: bool,
467    had_empty_match: bool,
468}
469
470impl<'a, P> SplitHelper<'a, P>
471where
472    P: JavaStrPattern,
473{
474    #[inline]
475    fn new(haystack: &'a JavaStr, pat: P, allow_trailing_empty: bool) -> Self {
476        Self {
477            start: 0,
478            end: haystack.len(),
479            haystack,
480            pat,
481            allow_trailing_empty,
482            finished: false,
483            had_empty_match: false,
484        }
485    }
486
487    #[inline]
488    fn get_end(&mut self) -> Option<&'a JavaStr> {
489        if !self.finished {
490            self.finished = true;
491
492            if self.allow_trailing_empty || self.end - self.start > 0 {
493                // SAFETY: `self.start` and `self.end` always lie on unicode boundaries.
494                let string = unsafe { self.haystack.get_unchecked(self.start..self.end) };
495                return Some(string);
496            }
497        }
498
499        None
500    }
501
502    #[inline]
503    fn next_match(&mut self) -> Option<(usize, usize)> {
504        // SAFETY: `self.start` always lies on a unicode boundary.
505        let substr = unsafe { self.haystack.get_unchecked(self.start..) };
506
507        let result = if self.had_empty_match {
508            // if we had an empty match before, we are going to find the empty match again.
509            // don't do that, search from the next index along.
510
511            if substr.is_empty() {
512                None
513            } else {
514                // SAFETY: we can pop the string because we already checked if the string is
515                // empty above
516                let first_char_len = unsafe { substr.chars().next().unwrap_unchecked().len_utf8() };
517                let popped_str = unsafe { substr.get_unchecked(first_char_len..) };
518
519                self.pat
520                    .find_in(popped_str)
521                    .map(|(index, len)| (index + first_char_len + self.start, len))
522            }
523        } else {
524            self.pat
525                .find_in(substr)
526                .map(|(index, len)| (index + self.start, len))
527        };
528
529        self.had_empty_match = result.is_some_and(|(_, len)| len == 0);
530
531        result
532    }
533
534    #[inline]
535    fn next(&mut self) -> Option<&'a JavaStr> {
536        if self.finished {
537            return None;
538        }
539
540        match self.next_match() {
541            Some((index, len)) => unsafe {
542                // SAFETY: pattern guarantees valid indices
543                let elt = self.haystack.get_unchecked(self.start..index);
544                self.start = index + len;
545                Some(elt)
546            },
547            None => self.get_end(),
548        }
549    }
550
551    #[inline]
552    fn next_inclusive(&mut self) -> Option<&'a JavaStr> {
553        if self.finished {
554            return None;
555        }
556
557        match self.next_match() {
558            Some((index, len)) => unsafe {
559                // SAFETY: pattern guarantees valid indices
560                let elt = self.haystack.get_unchecked(self.start..index + len);
561                self.start = index + len;
562                Some(elt)
563            },
564            None => self.get_end(),
565        }
566    }
567
568    #[inline]
569    fn next_match_back(&mut self) -> Option<(usize, usize)> {
570        // SAFETY: `self.end` always lies on a unicode boundary.
571        let substr = unsafe { self.haystack.get_unchecked(..self.end) };
572
573        let result = if self.had_empty_match {
574            // if we had an empty match before, we are going to find the empty match again.
575            // don't do that, search from the next index along.
576
577            if substr.is_empty() {
578                None
579            } else {
580                // SAFETY: we can pop the string because we already checked if the string is
581                // empty above
582                let last_char_len =
583                    unsafe { substr.chars().next_back().unwrap_unchecked().len_utf8() };
584                let popped_str = unsafe { substr.get_unchecked(..substr.len() - last_char_len) };
585
586                self.pat.rfind_in(popped_str)
587            }
588        } else {
589            self.pat.rfind_in(substr)
590        };
591
592        self.had_empty_match = result.is_some_and(|(_, len)| len == 0);
593
594        result
595    }
596
597    #[inline]
598    fn next_back(&mut self) -> Option<&'a JavaStr> {
599        if self.finished {
600            return None;
601        }
602
603        if !self.allow_trailing_empty {
604            self.allow_trailing_empty = true;
605            match self.next_back() {
606                Some(elt) if !elt.is_empty() => return Some(elt),
607                _ => {
608                    if self.finished {
609                        return None;
610                    }
611                }
612            }
613        }
614
615        match self.next_match_back() {
616            Some((index, len)) => unsafe {
617                // SAFETY: pattern guarantees valid indices
618                let elt = self.haystack.get_unchecked(index + len..self.end);
619                self.end = index;
620                Some(elt)
621            },
622            None => unsafe {
623                // SAFETY: `self.start` and `self.end` always lie on unicode boundaries.
624                self.finished = true;
625                Some(self.haystack.get_unchecked(self.start..self.end))
626            },
627        }
628    }
629
630    #[inline]
631    fn next_back_inclusive(&mut self) -> Option<&'a JavaStr> {
632        if self.finished {
633            return None;
634        }
635
636        if !self.allow_trailing_empty {
637            self.allow_trailing_empty = true;
638            match self.next_back_inclusive() {
639                Some(elt) if !elt.is_empty() => return Some(elt),
640                _ => {
641                    if self.finished {
642                        return None;
643                    }
644                }
645            }
646        }
647
648        match self.next_match_back() {
649            Some((index, len)) => {
650                // SAFETY: pattern guarantees valid indices
651                let elt = unsafe { self.haystack.get_unchecked(index + len..self.end) };
652                self.end = index + len;
653                Some(elt)
654            }
655            None => {
656                self.finished = true;
657                // SAFETY: `self.start` and `self.end` always lie on unicode boundaries.
658                Some(unsafe { self.haystack.get_unchecked(self.start..self.end) })
659            }
660        }
661    }
662}
663
664#[derive(Clone, Debug)]
665pub struct Split<'a, P> {
666    inner: SplitHelper<'a, P>,
667}
668
669impl<'a, P> Split<'a, P>
670where
671    P: JavaStrPattern,
672{
673    #[inline]
674    pub(crate) fn new(haystack: &'a JavaStr, pat: P) -> Self {
675        Split {
676            inner: SplitHelper::new(haystack, pat, true),
677        }
678    }
679}
680
681impl<'a, P> Iterator for Split<'a, P>
682where
683    P: JavaStrPattern,
684{
685    type Item = &'a JavaStr;
686
687    #[inline]
688    fn next(&mut self) -> Option<Self::Item> {
689        self.inner.next()
690    }
691}
692
693impl<P> DoubleEndedIterator for Split<'_, P>
694where
695    P: JavaStrPattern,
696{
697    #[inline]
698    fn next_back(&mut self) -> Option<Self::Item> {
699        self.inner.next_back()
700    }
701}
702
703impl<P> FusedIterator for Split<'_, P> where P: JavaStrPattern {}
704
705#[derive(Clone, Debug)]
706pub struct RSplit<'a, P> {
707    inner: SplitHelper<'a, P>,
708}
709
710impl<'a, P> RSplit<'a, P>
711where
712    P: JavaStrPattern,
713{
714    #[inline]
715    pub(crate) fn new(haystack: &'a JavaStr, pat: P) -> Self {
716        RSplit {
717            inner: SplitHelper::new(haystack, pat, true),
718        }
719    }
720}
721
722impl<'a, P> Iterator for RSplit<'a, P>
723where
724    P: JavaStrPattern,
725{
726    type Item = &'a JavaStr;
727
728    #[inline]
729    fn next(&mut self) -> Option<Self::Item> {
730        self.inner.next_back()
731    }
732}
733
734impl<P> DoubleEndedIterator for RSplit<'_, P>
735where
736    P: JavaStrPattern,
737{
738    #[inline]
739    fn next_back(&mut self) -> Option<Self::Item> {
740        self.inner.next()
741    }
742}
743
744impl<P> FusedIterator for RSplit<'_, P> where P: JavaStrPattern {}
745
746#[derive(Clone, Debug)]
747pub struct SplitTerminator<'a, P> {
748    inner: SplitHelper<'a, P>,
749}
750
751impl<'a, P> SplitTerminator<'a, P>
752where
753    P: JavaStrPattern,
754{
755    #[inline]
756    pub(crate) fn new(haystack: &'a JavaStr, pat: P) -> Self {
757        SplitTerminator {
758            inner: SplitHelper::new(haystack, pat, false),
759        }
760    }
761}
762
763impl<'a, P> Iterator for SplitTerminator<'a, P>
764where
765    P: JavaStrPattern,
766{
767    type Item = &'a JavaStr;
768
769    #[inline]
770    fn next(&mut self) -> Option<Self::Item> {
771        self.inner.next()
772    }
773}
774
775impl<P> DoubleEndedIterator for SplitTerminator<'_, P>
776where
777    P: JavaStrPattern,
778{
779    #[inline]
780    fn next_back(&mut self) -> Option<Self::Item> {
781        self.inner.next_back()
782    }
783}
784
785impl<P> FusedIterator for SplitTerminator<'_, P> where P: JavaStrPattern {}
786
787#[derive(Clone, Debug)]
788pub struct RSplitTerminator<'a, P> {
789    inner: SplitHelper<'a, P>,
790}
791
792impl<'a, P> RSplitTerminator<'a, P>
793where
794    P: JavaStrPattern,
795{
796    #[inline]
797    pub(crate) fn new(haystack: &'a JavaStr, pat: P) -> Self {
798        RSplitTerminator {
799            inner: SplitHelper::new(haystack, pat, false),
800        }
801    }
802}
803
804impl<'a, P> Iterator for RSplitTerminator<'a, P>
805where
806    P: JavaStrPattern,
807{
808    type Item = &'a JavaStr;
809
810    #[inline]
811    fn next(&mut self) -> Option<Self::Item> {
812        self.inner.next_back()
813    }
814}
815
816impl<P> DoubleEndedIterator for RSplitTerminator<'_, P>
817where
818    P: JavaStrPattern,
819{
820    #[inline]
821    fn next_back(&mut self) -> Option<Self::Item> {
822        self.inner.next()
823    }
824}
825
826impl<P> FusedIterator for RSplitTerminator<'_, P> where P: JavaStrPattern {}
827
828#[derive(Clone, Debug)]
829pub struct SplitInclusive<'a, P> {
830    inner: SplitHelper<'a, P>,
831}
832
833impl<'a, P> SplitInclusive<'a, P>
834where
835    P: JavaStrPattern,
836{
837    #[inline]
838    pub(crate) fn new(haystack: &'a JavaStr, pat: P) -> Self {
839        SplitInclusive {
840            inner: SplitHelper::new(haystack, pat, false),
841        }
842    }
843}
844
845impl<'a, P> Iterator for SplitInclusive<'a, P>
846where
847    P: JavaStrPattern,
848{
849    type Item = &'a JavaStr;
850
851    #[inline]
852    fn next(&mut self) -> Option<Self::Item> {
853        self.inner.next_inclusive()
854    }
855}
856
857impl<P> DoubleEndedIterator for SplitInclusive<'_, P>
858where
859    P: JavaStrPattern,
860{
861    #[inline]
862    fn next_back(&mut self) -> Option<Self::Item> {
863        self.inner.next_back_inclusive()
864    }
865}
866
867impl<P> FusedIterator for SplitInclusive<'_, P> where P: JavaStrPattern {}
868
869#[derive(Clone, Debug)]
870pub struct SplitN<'a, P> {
871    inner: SplitHelper<'a, P>,
872    count: usize,
873}
874
875impl<'a, P> SplitN<'a, P>
876where
877    P: JavaStrPattern,
878{
879    #[inline]
880    pub(crate) fn new(haystack: &'a JavaStr, pat: P, count: usize) -> Self {
881        SplitN {
882            inner: SplitHelper::new(haystack, pat, true),
883            count,
884        }
885    }
886}
887
888impl<'a, P> Iterator for SplitN<'a, P>
889where
890    P: JavaStrPattern,
891{
892    type Item = &'a JavaStr;
893
894    #[inline]
895    fn next(&mut self) -> Option<Self::Item> {
896        match self.count {
897            0 => None,
898            1 => {
899                self.count = 0;
900                self.inner.get_end()
901            }
902            _ => {
903                self.count -= 1;
904                self.inner.next()
905            }
906        }
907    }
908}
909
910impl<P> FusedIterator for SplitN<'_, P> where P: JavaStrPattern {}
911
912#[derive(Clone, Debug)]
913pub struct RSplitN<'a, P> {
914    inner: SplitHelper<'a, P>,
915    count: usize,
916}
917
918impl<'a, P> RSplitN<'a, P>
919where
920    P: JavaStrPattern,
921{
922    #[inline]
923    pub(crate) fn new(haystack: &'a JavaStr, pat: P, count: usize) -> Self {
924        RSplitN {
925            inner: SplitHelper::new(haystack, pat, true),
926            count,
927        }
928    }
929}
930
931impl<'a, P> Iterator for RSplitN<'a, P>
932where
933    P: JavaStrPattern,
934{
935    type Item = &'a JavaStr;
936
937    #[inline]
938    fn next(&mut self) -> Option<Self::Item> {
939        match self.count {
940            0 => None,
941            1 => {
942                self.count = 0;
943                self.inner.get_end()
944            }
945            _ => {
946                self.count -= 1;
947                self.inner.next_back()
948            }
949        }
950    }
951}
952
953impl<P> FusedIterator for RSplitN<'_, P> where P: JavaStrPattern {}
954
955#[derive(Clone, Debug)]
956pub struct SplitAsciiWhitespace<'a> {
957    #[allow(clippy::type_complexity)]
958    pub(crate) inner: Map<
959        Filter<slice::Split<'a, u8, fn(&u8) -> bool>, fn(&&[u8]) -> bool>,
960        fn(&[u8]) -> &JavaStr,
961    >,
962}
963delegate!(Iterator for SplitAsciiWhitespace<'a> => &'a JavaStr);
964delegate!(DoubleEndedIterator for SplitAsciiWhitespace<'a>);
965delegate!(FusedIterator for SplitAsciiWhitespace<'a>);
966
967#[derive(Clone, Debug)]
968pub struct SplitWhitespace<'a> {
969    #[allow(clippy::type_complexity)]
970    pub(crate) inner: Filter<Split<'a, fn(JavaCodePoint) -> bool>, fn(&&JavaStr) -> bool>,
971}
972delegate!(Iterator for SplitWhitespace<'a> => &'a JavaStr);
973delegate!(DoubleEndedIterator for SplitWhitespace<'a>);
974delegate!(FusedIterator for SplitWhitespace<'a>);