1use alloc::borrow::Cow;
75use alloc::boxed::Box;
76use alloc::string::String;
77use core::borrow::{Borrow, BorrowMut};
78use core::cmp::Ordering;
79use core::convert::Infallible;
80use core::fmt::{Debug, Display, Formatter, Result as FmtResult, Write};
81use core::hash::{Hash, Hasher};
82use core::iter::{self, FromIterator};
83use core::ops::{Add, AddAssign, Deref, DerefMut, Index, IndexMut};
84use core::str::{self, FromStr};
85
86#[cfg(feature = "std")]
87use std::error::Error;
88
89use bytes::{Bytes, BytesMut};
90use either::Either;
91
92#[cfg(feature = "serde")]
93mod serde_impl;
94
95#[derive(Copy, Clone, Debug)]
97pub struct Utf8Error<S> {
98 e: core::str::Utf8Error,
99 inner: S,
100}
101
102impl<S> Utf8Error<S> {
103 pub fn into_inner(self) -> S {
105 self.inner
106 }
107
108 pub fn utf8_error(&self) -> str::Utf8Error {
110 self.e
111 }
112}
113
114impl<S> Display for Utf8Error<S> {
115 fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
116 Display::fmt(&self.e, fmt)
117 }
118}
119
120#[cfg(feature = "std")]
121impl<S: Debug> Error for Utf8Error<S> {}
122
123#[derive(Copy, Clone, Debug, Eq, PartialEq)]
127pub enum Direction {
128 Forward,
130
131 Backward,
133}
134
135#[derive(Clone, Debug)]
140pub struct BytesIter<S, F> {
141 bytes: Option<S>,
142 extract: F,
143 direction: Direction,
144}
145
146impl<S, F> BytesIter<S, F>
147where
148 S: Storage,
149 F: FnMut(&str) -> Option<(usize, usize)>,
150{
151 pub fn new(s: StrInner<S>, direction: Direction, ext: F) -> Self {
166 Self {
167 bytes: Some(s.0),
168 extract: ext,
169 direction,
170 }
171 }
172}
173
174impl<S, F> Iterator for BytesIter<S, F>
175where
176 S: Storage,
177 F: FnMut(&str) -> Option<(usize, usize)>,
178{
179 type Item = StrInner<S>;
180
181 fn next(&mut self) -> Option<StrInner<S>> {
182 let storage = self.bytes.take()?;
183 let whole_str = unsafe { str::from_utf8_unchecked(storage.as_ref()) };
185 fn split<S: Storage>(storage: S, left: usize, right: usize) -> (S, S) {
186 let whole_str = unsafe { str::from_utf8_unchecked(storage.as_ref()) };
187 assert!(whole_str.is_char_boundary(left));
190 assert!(whole_str.is_char_boundary(right));
191
192 let (with_sep, end) = storage.split_at(right);
194 let (start, _sep) = with_sep.split_at(left);
195 (start, end)
196 }
197 match ((self.extract)(whole_str), self.direction) {
198 (Some((chunk_end, sep_end)), Direction::Forward) => {
199 assert!(chunk_end <= sep_end);
200 let (start, end) = split(storage, chunk_end, sep_end);
201
202 self.bytes = Some(end);
203 Some(StrInner(start))
204 }
205 (Some((chunk_start, sep_start)), Direction::Backward) => {
206 assert!(sep_start <= chunk_start);
207 let (start, end) = split(storage, sep_start, chunk_start);
208
209 self.bytes = Some(start);
210 Some(StrInner(end))
211 }
212 (None, _) => {
213 Some(StrInner(storage))
215 }
216 }
217 }
218}
219
220fn sep_find<F: Fn(char) -> bool>(s: &str, is_sep: F) -> Option<(usize, usize)> {
222 let sep_start = s.find(&is_sep)?;
223 let sep_end = s[sep_start..]
224 .find(|c| !is_sep(c))
225 .map(|e| e + sep_start)
226 .unwrap_or_else(|| s.len());
227 Some((sep_start, sep_end))
228}
229
230fn empty_sep(s: &str, limit: usize) -> Option<(usize, usize)> {
232 let char_end = s
233 .char_indices()
234 .skip(1)
235 .map(|(i, _)| i)
236 .chain(iter::once(s.len()).take((!s.is_empty()) as usize))
237 .take(limit)
238 .next()?;
239 Some((char_end, char_end))
240}
241
242fn rempty_sep(s: &str, limit: usize) -> Option<(usize, usize)> {
243 let char_start = s.char_indices().rev().map(|(i, _)| i).take(limit).next()?;
244 Some((char_start, char_start))
245}
246
247pub unsafe trait Storage: AsRef<[u8]> + Default + Sized {
257 type Creator: Default + StorageMut;
262
263 fn from_creator(creator: Self::Creator) -> Self;
267
268 fn split_at(self, at: usize) -> (Self, Self);
270}
271
272unsafe impl Storage for Bytes {
273 type Creator = BytesMut;
274 fn from_creator(creator: Self::Creator) -> Self {
275 creator.freeze()
276 }
277 fn split_at(mut self, at: usize) -> (Self, Self) {
278 let right = self.split_off(at);
279 (self, right)
280 }
281}
282
283unsafe impl Storage for BytesMut {
284 type Creator = BytesMut;
285 fn from_creator(creator: Self::Creator) -> Self {
286 creator
287 }
288 fn split_at(mut self, at: usize) -> (Self, Self) {
289 let right = self.split_off(at);
290 (self, right)
291 }
292}
293
294pub unsafe trait StorageMut: Storage + AsMut<[u8]> {
303 type Immutable: Storage<Creator = Self>;
305
306 fn push_slice(&mut self, s: &[u8]);
308}
309
310unsafe impl StorageMut for BytesMut {
311 type Immutable = Bytes;
312 fn push_slice(&mut self, s: &[u8]) {
313 self.extend_from_slice(s)
314 }
315}
316
317#[derive(Clone, Default)]
323pub struct StrInner<S>(S);
324
325impl<S: Storage> StrInner<S> {
326 pub fn new() -> Self {
328 Self::default()
329 }
330
331 pub fn into_inner(self) -> S {
333 self.0
334 }
335
336 pub fn inner(&self) -> &S {
338 &self.0
339 }
340
341 pub fn from_inner(s: S) -> Result<Self, Utf8Error<S>> {
347 match str::from_utf8(s.as_ref()) {
348 Ok(_) => Ok(Self(s)),
349 Err(e) => Err(Utf8Error { e, inner: s }),
350 }
351 }
352
353 pub const unsafe fn from_inner_unchecked(s: S) -> Self {
359 Self(s)
360 }
361
362 pub fn split_at_bytes(self, at: usize) -> (Self, Self) {
368 assert!(self.deref().is_char_boundary(at));
369 let (l, r) = self.0.split_at(at);
370 (Self(l), Self(r))
371 }
372
373 pub fn split_whitespace_bytes(self) -> impl Iterator<Item = Self> {
378 BytesIter::new(self, Direction::Forward, |s| {
379 sep_find(s, char::is_whitespace)
380 })
381 .filter(|s| !s.is_empty())
382 }
383
384 pub fn split_ascii_whitespace_bytes(self) -> impl Iterator<Item = Self> {
389 BytesIter::new(self, Direction::Forward, |s| {
390 sep_find(s, |c| c.is_ascii() && (c as u8).is_ascii_whitespace())
391 })
392 .filter(|s| !s.is_empty())
393 }
394
395 pub fn lines_bytes(self) -> impl Iterator<Item = Self> {
400 if self.is_empty() {
401 Either::Left(iter::empty())
402 } else {
403 let iter = BytesIter::new(self, Direction::Forward, |s| sep_find(s, |c| c == '\n'))
404 .map(|s| match s.chars().next() {
405 Some('\r') => s.split_at_bytes(1).1,
406 _ => s,
407 });
408 Either::Right(iter)
409 }
410 }
411
412 pub fn split_bytes<'s>(self, sep: &'s str) -> impl Iterator<Item = Self> + 's
418 where
419 S: 's,
420 {
421 if sep.is_empty() {
422 let bulk = BytesIter::new(self, Direction::Forward, |s| empty_sep(s, usize::MAX));
423 Either::Left(iter::once(Self::default()).chain(bulk))
424 } else {
425 let sep_find = move |s: &str| s.find(sep).map(|pos| (pos, pos + sep.len()));
426 Either::Right(BytesIter::new(self, Direction::Forward, sep_find))
427 }
428 }
429
430 pub fn splitn_bytes<'s>(self, mut n: usize, sep: &'s str) -> impl Iterator<Item = Self> + 's
436 where
437 S: 's,
438 {
439 if sep.is_empty() {
441 if n <= 1 {
442 Either::Left(Either::Left(iter::once(self).take(n)))
443 } else {
444 n -= 1;
445 let bulk = BytesIter::new(self, Direction::Forward, move |s| {
446 n -= 1;
447 empty_sep(s, n)
448 });
449 Either::Left(Either::Right(iter::once(Self::default()).chain(bulk)))
450 }
451 } else {
452 let sep_find = move |s: &str| {
453 n -= 1;
454 if n == 0 {
455 None
456 } else {
457 s.find(sep).map(|pos| (pos, pos + sep.len()))
458 }
459 };
460 Either::Right(BytesIter::new(self, Direction::Forward, sep_find).take(n))
461 }
462 }
463
464 pub fn rsplit_bytes<'s>(self, sep: &'s str) -> impl Iterator<Item = Self> + 's
466 where
467 S: 's,
468 {
469 if sep.is_empty() {
470 let bulk = BytesIter::new(self, Direction::Backward, |s| rempty_sep(s, usize::MAX));
471 Either::Left(iter::once(Self::default()).chain(bulk))
472 } else {
473 let sep_find = move |s: &str| s.rfind(sep).map(|pos| (pos + sep.len(), pos));
474 Either::Right(BytesIter::new(self, Direction::Backward, sep_find))
475 }
476 }
477
478 pub fn rsplitn_bytes<'s>(self, mut n: usize, sep: &'s str) -> impl Iterator<Item = Self> + 's
480 where
481 S: 's,
482 {
483 if sep.is_empty() {
485 if n <= 1 {
486 Either::Left(Either::Left(iter::once(self).take(n)))
487 } else {
488 n -= 1;
489 let bulk = BytesIter::new(self, Direction::Backward, move |s| {
490 n -= 1;
491 rempty_sep(s, n)
492 });
493 Either::Left(Either::Right(iter::once(Self::default()).chain(bulk)))
494 }
495 } else {
496 let sep_find = move |s: &str| {
497 n -= 1;
498 if n == 0 {
499 None
500 } else {
501 s.rfind(sep).map(|pos| (pos + sep.len(), pos))
502 }
503 };
504 Either::Right(BytesIter::new(self, Direction::Backward, sep_find).take(n))
505 }
506 }
507}
508
509impl<S: StorageMut> StrInner<S> {
510 pub fn push_str(&mut self, s: &str) {
512 self.0.push_slice(s.as_bytes());
513 }
514
515 pub fn push(&mut self, c: char) {
517 self.push_str(c.encode_utf8(&mut [0; 4]));
518 }
519
520 pub unsafe fn inner_mut(&mut self) -> &mut S {
526 &mut self.0
527 }
528
529 pub fn freeze(self) -> StrInner<S::Immutable> {
533 StrInner(S::Immutable::from_creator(self.0))
534 }
535}
536
537impl<S: Storage> Deref for StrInner<S> {
538 type Target = str;
539
540 fn deref(&self) -> &str {
541 unsafe { str::from_utf8_unchecked(self.0.as_ref()) }
542 }
543}
544
545impl<S: StorageMut> DerefMut for StrInner<S> {
546 fn deref_mut(&mut self) -> &mut str {
547 unsafe { str::from_utf8_unchecked_mut(self.0.as_mut()) }
548 }
549}
550
551impl<S, T> AsRef<T> for StrInner<S>
552where
553 S: Storage,
554 str: AsRef<T>,
555{
556 fn as_ref(&self) -> &T {
557 self.deref().as_ref()
558 }
559}
560
561impl<S: StorageMut> AsMut<str> for StrInner<S> {
562 fn as_mut(&mut self) -> &mut str {
563 self.deref_mut()
564 }
565}
566
567impl<S: Storage> Borrow<str> for StrInner<S> {
568 fn borrow(&self) -> &str {
569 self.deref()
570 }
571}
572
573impl<S: StorageMut> BorrowMut<str> for StrInner<S> {
574 fn borrow_mut(&mut self) -> &mut str {
575 self.deref_mut()
576 }
577}
578
579impl<S: Storage> Debug for StrInner<S> {
580 fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
581 Debug::fmt(self.deref(), fmt)
582 }
583}
584
585impl<S: Storage> Display for StrInner<S> {
586 fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
587 Display::fmt(self.deref(), fmt)
588 }
589}
590
591impl<S: Storage> Hash for StrInner<S> {
592 fn hash<H: Hasher>(&self, state: &mut H) {
593 self.deref().hash(state)
594 }
595}
596
597impl<S, I> Index<I> for StrInner<S>
598where
599 S: Storage,
600 str: Index<I>,
601{
602 type Output = <str as Index<I>>::Output;
603
604 fn index(&self, index: I) -> &Self::Output {
605 self.deref().index(index)
606 }
607}
608
609impl<S, I> IndexMut<I> for StrInner<S>
610where
611 S: StorageMut,
612 str: IndexMut<I>,
613{
614 fn index_mut(&mut self, index: I) -> &mut Self::Output {
615 self.deref_mut().index_mut(index)
616 }
617}
618
619impl<S: StorageMut> Add<&str> for StrInner<S> {
620 type Output = Self;
621
622 fn add(mut self, rhs: &str) -> Self::Output {
623 self.push_str(rhs);
624 self
625 }
626}
627
628impl<S: StorageMut> AddAssign<&str> for StrInner<S> {
629 fn add_assign(&mut self, rhs: &str) {
630 self.push_str(rhs);
631 }
632}
633
634impl<S: StorageMut> Extend<char> for StrInner<S> {
635 fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
636 for c in iter {
637 self.push(c);
638 }
639 }
640}
641
642impl<'a, S: StorageMut> Extend<&'a char> for StrInner<S> {
643 fn extend<T: IntoIterator<Item = &'a char>>(&mut self, iter: T) {
644 for c in iter {
645 self.push(*c);
646 }
647 }
648}
649
650macro_rules! impl_extend {
651 ($ty:ty $(, $lifetimes:lifetime )* ) => {
652 impl<$($lifetimes, )* S: StorageMut> Extend<$ty> for StrInner<S> {
653 fn extend<T: IntoIterator<Item = $ty>>(&mut self, iter: T) {
654 for i in iter {
655 self.push_str(i.as_ref());
656 }
657 }
658 }
659
660 impl<$($lifetimes, )* S> FromIterator<$ty> for StrInner<S>
661 where
662 S: Storage,
663 {
664 fn from_iter<T: IntoIterator<Item = $ty>>(iter: T) -> Self {
665 let mut creator = StrInner(S::Creator::default());
666 creator.extend(iter);
667 StrInner(S::from_creator(creator.0))
668 }
669 }
670 };
671}
672
673impl_extend!(String);
674impl_extend!(Box<str>);
675impl_extend!(&'a String, 'a);
676impl_extend!(&'a str, 'a);
677impl_extend!(Cow<'a, str>, 'a);
678
679macro_rules! impl_from {
680 ($ty:ty $(, $lifetimes:lifetime )* ) => {
681 impl<$($lifetimes, )* S> From<$ty> for StrInner<S>
682 where
683 S: Storage,
684 {
685 fn from(s: $ty) -> Self {
686 iter::once(s).collect()
687 }
688 }
689 };
690}
691
692impl_from!(&'a String, 'a);
693impl_from!(&'a str, 'a);
694impl_from!(Cow<'a, str>, 'a);
695
696impl From<String> for Str {
697 fn from(s: String) -> Self {
698 let inner = Bytes::from(s.into_bytes());
699 unsafe { Str::from_inner_unchecked(inner) }
701 }
702}
703
704impl From<Box<str>> for Str {
705 fn from(s: Box<str>) -> Self {
706 let s: Box<[u8]> = s.into();
707 let inner = Bytes::from(s);
708 unsafe { Str::from_inner_unchecked(inner) }
710 }
711}
712
713macro_rules! impl_try_from {
714 ($ty: ty) => {
715 impl TryFrom<$ty> for StrInner<$ty> {
716 type Error = Utf8Error<$ty>;
717 fn try_from(s: $ty) -> Result<Self, Utf8Error<$ty>> {
718 Self::from_inner(s)
719 }
720 }
721
722 impl From<StrInner<$ty>> for $ty {
723 fn from(s: StrInner<$ty>) -> $ty {
724 s.0
725 }
726 }
727 };
728}
729
730impl_try_from!(Bytes);
731impl_try_from!(BytesMut);
732
733impl From<StrMut> for Str {
734 fn from(s: StrMut) -> Self {
735 s.freeze()
736 }
737}
738
739impl<S: Storage> FromStr for StrInner<S> {
740 type Err = Infallible;
741
742 fn from_str(s: &str) -> Result<Self, Self::Err> {
743 Ok(s.into())
744 }
745}
746
747impl<S: Storage> PartialEq for StrInner<S> {
748 fn eq(&self, other: &Self) -> bool {
749 self.deref() == other.deref()
750 }
751}
752
753impl<S: Storage> Eq for StrInner<S> {}
754
755impl<S: Storage> PartialOrd for StrInner<S> {
756 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
757 Some(Ord::cmp(self, other))
758 }
759}
760
761impl<S: Storage> Ord for StrInner<S> {
762 fn cmp(&self, other: &Self) -> Ordering {
763 self.deref().cmp(other.deref())
764 }
765}
766
767macro_rules! impl_partrial_eq {
768 ($ty: ty $(, $lifetimes:lifetime )* ) => {
769 impl<$($lifetimes, )* S: Storage> PartialEq<$ty> for StrInner<S> {
770 fn eq(&self, other: &$ty) -> bool {
771 self.deref() == other.deref()
772 }
773 }
774
775 impl<$($lifetimes, )* S: Storage> PartialEq<StrInner<S>> for $ty {
776 fn eq(&self, other: &StrInner<S>) -> bool {
777 self.deref() == other.deref()
778 }
779 }
780
781 impl<$($lifetimes, )* S: Storage> PartialOrd<$ty> for StrInner<S> {
782 fn partial_cmp(&self, other: &$ty) -> Option<Ordering> {
783 Some(self.deref().cmp(other.deref()))
784 }
785 }
786
787 impl<$($lifetimes, )* S: Storage> PartialOrd<StrInner<S>> for $ty {
788 fn partial_cmp(&self, other: &StrInner<S>) -> Option<Ordering> {
789 Some(self.deref().cmp(other.deref()))
790 }
791 }
792 };
793}
794
795impl_partrial_eq!(String);
796impl_partrial_eq!(Box<str>);
797impl_partrial_eq!(&'a str, 'a);
798impl_partrial_eq!(&'a mut str, 'a);
799impl_partrial_eq!(Cow<'a, str>, 'a);
800
801impl<S: StorageMut> Write for StrInner<S> {
802 fn write_str(&mut self, s: &str) -> FmtResult {
803 self.push_str(s);
804 Ok(())
805 }
806}
807#[macro_export]
817macro_rules! format_bytes {
818 ($($arg: tt)*) => {
819 $crate::format_bytes_mut!($($arg)*).freeze()
820 }
821}
822
823#[macro_export]
833macro_rules! format_bytes_mut {
834 ($($arg: tt)*) => {{
835 use std::fmt::Write;
836 let mut buf = $crate::StrMut::default();
837 write!(buf, $($arg)*).unwrap();
838 buf
839 }}
840}
841
842pub type Str = StrInner<Bytes>;
847
848impl Str {
849 pub fn slice<R>(&self, range: R) -> Str
855 where
856 str: Index<R, Output = str>,
857 {
858 self.slice_ref(&self[range])
859 }
860
861 pub fn slice_ref(&self, subslice: &str) -> Self {
886 let sub = self.0.slice_ref(subslice.as_bytes());
887 Self(sub)
888 }
889
890 pub const fn from_static(s: &'static str) -> Self {
892 let bytes = Bytes::from_static(s.as_bytes());
893 unsafe { Str::from_inner_unchecked(bytes) }
895 }
896}
897
898pub type StrMut = StrInner<BytesMut>;
906
907impl StrMut {
908 pub fn split_built(&mut self) -> StrMut {
910 StrInner(self.0.split())
911 }
912}
913
914#[cfg(test)]
915mod tests {
916 use itertools::Itertools;
917 use std::panic;
918
919 use super::*;
920
921 #[test]
922 fn split_w_byte_index() {
923 let v = Str::from("😈 ").split_whitespace_bytes().collect_vec();
924 assert_eq!(1, v.len());
925 assert_eq!("😈", v[0]);
926 }
927
928 #[test]
929 fn split_same() {
930 let v = Str::from("a").split_bytes("a").collect_vec();
931 assert_eq!(2, v.len());
932 assert_eq!("", v[0]);
933 assert_eq!("", v[1]);
934 }
935
936 #[test]
937 fn split_empty_pat() {
938 let v = Str::from("a").split_bytes("").collect_vec();
939 assert_eq!(3, v.len());
940 assert_eq!("", v[0]);
941 assert_eq!("a", v[1]);
942 assert_eq!("", v[2]);
943 }
944
945 #[test]
946 fn slice_checks_char_boundaries() {
947 let v = Str::from("😈");
948 assert_eq!(4, v.len());
949 panic::catch_unwind(|| v.slice(1..)).unwrap_err();
950 }
951
952 #[test]
953 fn split_at_bytes_mid() {
954 let v = Str::from("hello");
955 let (l, r) = v.split_at_bytes(2);
956 assert_eq!("he", l);
957 assert_eq!("llo", r);
958 }
959
960 #[test]
961 fn split_at_bytes_begin() {
962 let v = Str::from("hello");
963 let (l, r) = v.split_at_bytes(0);
964 assert_eq!("", l);
965 assert_eq!("hello", r);
966 }
967
968 #[test]
969 fn split_at_bytes_end() {
970 let v = Str::from("hello");
971 let (l, r) = v.split_at_bytes(5);
972 assert_eq!("hello", l);
973 assert_eq!("", r);
974 }
975
976 #[test]
977 fn split_at_bytes_panic() {
978 let v = Str::from("😈");
979 assert_eq!(4, v.len());
980 panic::catch_unwind(|| v.split_at_bytes(2)).unwrap_err();
981 }
982
983 #[cfg(not(miri))]
984 mod proptests {
985 use proptest::prelude::*;
986
987 use super::*;
988
989 proptest! {
990 #[test]
991 fn split_whitespace(s: String) {
992 let bstring = Str::from(&s);
993
994 let bw = bstring.split_whitespace_bytes();
995 let sw = s.split_whitespace();
996
997 for (b, s) in bw.zip_eq(sw) {
998 prop_assert_eq!(b, s);
999 }
1000 }
1001
1002 #[test]
1003 fn split_ascii_whitespace(s: String) {
1004 let bstring = Str::from(&s);
1005
1006 let bw = bstring.split_ascii_whitespace_bytes();
1007 let sw = s.split_ascii_whitespace();
1008
1009 for (b, s) in bw.zip_eq(sw) {
1010 prop_assert_eq!(b, s);
1011 }
1012 }
1013
1014 #[test]
1015 fn lines(s: String) {
1016 let bstring = Str::from(&s);
1017
1018 let bl = bstring.lines_bytes();
1019 let sl = s.lines();
1020
1021 for (b, s) in bl.zip_eq(sl) {
1022 prop_assert_eq!(b, s);
1023 }
1024 }
1025
1026 #[test]
1027 fn split(s: String, pat: String) {
1028 let bstring = Str::from(&s);
1029
1030 let bs = bstring.split_bytes(&pat);
1031 let ss = s.split(&pat);
1032
1033 for (b, s) in bs.zip_eq(ss) {
1034 prop_assert_eq!(b, s);
1035 }
1036 }
1037
1038 #[test]
1039 fn split_n(s: String, pat: String, n in 0..5usize) {
1040 let bstring = Str::from(&s);
1041
1042 let bs = bstring.splitn_bytes(n, &pat);
1043 let ss = s.splitn(n, &pat);
1044
1045 for (b, s) in bs.zip_eq(ss) {
1046 prop_assert_eq!(b, s);
1047 }
1048 }
1049
1050 #[test]
1051 fn rsplit(s: String, pat: String) {
1052 let bstring = Str::from(&s);
1053
1054 let bs = bstring.rsplit_bytes(&pat);
1055 let ss = s.rsplit(&pat);
1056
1057 for (b, s) in bs.zip_eq(ss) {
1058 prop_assert_eq!(b, s);
1059 }
1060 }
1061
1062 #[test]
1063 fn rsplit_n(s: String, pat: String, n in 0..5usize) {
1064 let bstring = Str::from(&s);
1065
1066 let bs = bstring.rsplitn_bytes(n, &pat);
1067 let ss = s.rsplitn(n, &pat);
1068
1069 for (b, s) in bs.zip_eq(ss) {
1070 prop_assert_eq!(b, s);
1071 }
1072 }
1073 }
1074 }
1075}