pub mod prelude {
pub use crate::{SplitEvery, SplitEveryImpl};
}
pub trait SplitEveryImpl: Sized {
fn split_every_n_times(self, pat: Self, n: usize) -> SplitEvery<Self, Self> {
SplitEvery {
input: self,
pat,
n,
ind: 0,
}
}
}
impl SplitEveryImpl for &str {}
impl SplitEveryImpl for String {}
impl<'a> SplitEveryImpl for std::string::Drain<'a> {}
impl<T: Clone + PartialEq> SplitEveryImpl for Vec<T> {}
impl<T: Clone + PartialEq> SplitEveryImpl for &[T] {}
pub struct SplitEvery<Input, Pattern> {
input: Input,
pat: Pattern,
n: usize,
ind: usize,
}
impl<Input: FnMut() -> Option<Pattern>, Pattern: PartialEq> SplitEvery<Input, Pattern> {
pub fn n_times_from_fn(input: Input, pat: Pattern, n: usize) -> SplitEvery<Input, Pattern> {
SplitEvery {
input,
pat,
n,
ind: 0,
}
}
}
impl<Input: FnMut() -> Option<Pattern>, Pattern: PartialEq> Iterator
for SplitEvery<Input, Pattern>
{
type Item = Vec<Pattern>;
fn next(&mut self) -> Option<Self::Item> {
if self.n == 0 {
let out: Vec<Pattern> = std::iter::repeat_with(|| (self.input)())
.take_while(Option::is_some)
.flatten()
.collect();
if out.is_empty() {
return None;
}
return Some(out);
}
let mut out: Vec<Pattern> = Vec::with_capacity(5);
'main: for ind in 0..self.n {
while let Some(val) = (self.input)() {
if val == self.pat {
if ind == unsafe { self.n.unchecked_sub(1) } {
break 'main;
}
out.push(val);
continue 'main;
}
out.push(val);
}
}
if out.is_empty() {
return None;
}
Some(out)
}
}
impl<Pattern: AsRef<str>> Iterator for SplitEvery<&str, Pattern> {
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
split_every_next_str_helper(self)
}
}
impl<Pattern: AsRef<str>> Iterator for SplitEvery<String, Pattern> {
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
split_every_next_str_helper(self)
}
}
impl<'a, Pattern: AsRef<str>> Iterator for SplitEvery<std::string::Drain<'a>, Pattern> {
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
split_every_next_str_helper(self)
}
}
fn split_every_next_str_helper<Input: AsRef<str>, Pattern: AsRef<str>>(
split_every: &mut SplitEvery<Input, Pattern>,
) -> Option<String> {
let input: &str = split_every.input.as_ref();
if split_every.ind == input.len() {
return None;
}
let pat: &str = split_every.pat.as_ref();
let iter_haystack: &str = unsafe { input.get_unchecked(split_every.ind..) };
let mut len: usize = 0;
for ind in 0..split_every.n {
let haystack: &str = unsafe { iter_haystack.get_unchecked(len..) };
if let Some(byte_ind) = haystack.find(pat) {
len = unsafe { len.unchecked_add(byte_ind).unchecked_add(pat.len()) };
continue;
}
if ind == 0 {
split_every.ind = input.len();
return Some(haystack.to_string());
}
break;
}
split_every.ind = unsafe { split_every.ind.unchecked_add(len) };
Some(unsafe { iter_haystack.get_unchecked(..len.unchecked_sub(pat.len())) }.to_string())
}
impl<T: Clone + PartialEq> Iterator for SplitEvery<Vec<T>, Vec<T>> {
type Item = Vec<T>;
fn next(&mut self) -> Option<Self::Item> {
let (ind, out): (usize, Option<Vec<T>>) =
split_every_next_arr_helper(self, &self.input, &self.pat);
self.ind = ind;
out
}
}
impl<T: Clone + PartialEq> Iterator for SplitEvery<&[T], &[T]> {
type Item = Vec<T>;
fn next(&mut self) -> Option<Self::Item> {
let (ind, out): (usize, Option<Vec<T>>) =
split_every_next_arr_helper(self, self.input, self.pat);
self.ind = ind;
out
}
}
fn split_every_next_arr_helper<T, U: Clone + PartialEq>(
split_every: &SplitEvery<T, T>,
input: &[U],
pat: &[U],
) -> (usize, Option<Vec<U>>) {
if split_every.ind == input.len() {
return (split_every.ind, None);
}
let iter_haystack: &[U] = unsafe { input.get_unchecked(split_every.ind..) };
let mut len: usize = 0;
for ind in 0..split_every.n {
if len == iter_haystack.len() {
break;
}
if let Some((ind, _)) = unsafe { iter_haystack.get_unchecked(len..) }
.windows(pat.len())
.enumerate()
.find(|(_, val)| val == &pat)
{
len = unsafe { len.unchecked_add(ind).unchecked_add(pat.len()) };
continue;
}
if ind == 0 {
return (input.len(), Some(iter_haystack.to_vec()));
}
break;
}
(
unsafe { split_every.ind.unchecked_add(len) },
Some(unsafe { iter_haystack.get_unchecked(..len.unchecked_sub(pat.len())) }.to_vec()),
)
}
#[test]
fn test() {
let mut splitter: SplitEvery<&str, &str> = "oh oh oh oh oh".split_every_n_times(" ", 2);
assert_eq!(splitter.next().unwrap(), "oh oh");
assert_eq!(splitter.next().unwrap(), "oh oh");
assert_eq!(splitter.next().unwrap(), "oh");
assert_eq!(splitter.next(), None);
let mut splitter: SplitEvery<&str, &str> = "a a a a".split_every_n_times("b", 2);
assert_eq!(splitter.next().unwrap(), "a a a a");
assert_eq!(splitter.next(), None);
let mut iter = [
["This", "is", "you"],
["This", "is", "me"],
["This", "is", "someone"],
["This", "is", "them"],
]
.iter()
.flatten()
.copied();
let mut splitter: SplitEvery<Box<dyn FnMut() -> Option<&'static str>>, &str> =
SplitEvery::<Box<dyn FnMut() -> Option<&'static str>>, &str>::n_times_from_fn(
Box::new(move || iter.next()),
"is",
2,
);
assert_eq!(splitter.next().unwrap(), vec!["This", "is", "you", "This"]);
assert_eq!(
splitter.next().unwrap(),
vec!["me", "This", "is", "someone", "This"]
);
assert_eq!(splitter.next().unwrap(), vec!["them"]);
assert_eq!(splitter.next(), None);
#[allow(clippy::type_complexity)]
let mut splitter: SplitEvery<&[(u8, u8)], &[(u8, u8)]> = [
(0, 0),
(0, 1),
(0, 0), (0, 0),
(0, 0), (0, 1),
(0, 0),
(0, 0), (0, 1),
]
.split_every_n_times(&[(0, 0)], 2);
assert_eq!(splitter.next().unwrap(), vec![(0, 0), (0, 1)]);
assert_eq!(splitter.next().unwrap(), vec![(0, 0)]);
assert_eq!(splitter.next().unwrap(), vec![(0, 1), (0, 0)]);
assert_eq!(splitter.next().unwrap(), vec![(0, 1)]);
assert_eq!(splitter.next(), None);
let mut splitter: SplitEvery<&str, &str> =
"Oh hi there I don't really know what to say".split_every_n_times(" ", 3);
assert_eq!(splitter.next().unwrap(), "Oh hi there");
assert_eq!(splitter.next().unwrap(), "I don't really");
assert_eq!(splitter.next().unwrap(), "know what to");
assert_eq!(splitter.next().unwrap(), "say");
assert_eq!(splitter.next(), None);
}