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