split_every/
lib.rs

1//! Split for every n occurrences of a pattern iteratively.
2//! This crate **helps you** split data for every `n` occurrences of a `pattern`.  
3//! It contains an exclusive `iterator`.
4//!
5//! # Examples
6//!
7//! ```rust
8//! use split_every::prelude::*;
9//!
10//! // This prints: [(0, 0), (0, 1)]
11//! //              [(0, 0)]
12//! //              [(0, 1), (0, 0)]
13//! //              [(0, 1)]
14//! let mut splitter: SplitEvery<&[(u8, u8)], &[(u8, u8)]> = [
15//!     (0, 0), (0, 1), (0, 0),
16//!     (0, 0), (0, 0), (0, 1),
17//!     (0, 0), (0, 0), (0, 1),
18//! ].split_every_n_times(&[(0, 0)], 2);
19//! println!("{:?}", splitter.next().unwrap());
20//! println!("{:?}", splitter.next().unwrap());
21//! println!("{:?}", splitter.next().unwrap());
22//! println!("{:?}", splitter.next().unwrap());
23//!
24//! // This prints: "Oh hi there"
25//! //              "I don't really"
26//! //              "know what to"
27//! //              "say".
28//! let mut splitter: SplitEvery<&str, &str> =
29//!     "Oh hi there I don't really know what to say".split_every_n_times(" ", 3);
30//! println!("{:?}", splitter.next().unwrap());
31//! println!("{:?}", splitter.next().unwrap());
32//! println!("{:?}", splitter.next().unwrap());
33//! println!("{:?}", splitter.next().unwrap());
34//!
35//! // This prints: ["This", "is", "you", "This"]
36//! //              ["me", "This", "is", "someone", "This"]
37//! //              ["them"]
38//! let mut splitter: SplitEvery<Box<dyn FnMut() -> Option<&'static str>>, &str> = [
39//!     ["This", "is", "you"],
40//!     ["This", "is", "me"],
41//!     ["This", "is", "someone"],
42//!     ["This", "is", "them"],
43//! ]
44//! .iter()
45//! .flatten()
46//! .copied()
47//! .split_every_n_times("is", 2);
48//! println!("{:?}", splitter.next().unwrap());
49//! println!("{:?}", splitter.next().unwrap());
50//! println!("{:?}", splitter.next().unwrap());
51//!
52//! // This prints: ["This", "is", "you", "This"]
53//! //              ["me", "This", "is", "someone", "This"]
54//! //              ["them"]
55//!
56//! let mut iter = [
57//!     ["This", "is", "you"],
58//!     ["This", "is", "me"],
59//!     ["This", "is", "someone"],
60//!     ["This", "is", "them"],
61//! ].iter().flatten().map(|val| *val);
62//! let mut splitter: SplitEvery<Box<dyn FnMut() -> Option<&'static str>>, &str> =
63//!     SplitEvery::n_times_from_fn(Box::new(move || iter.next()), "is", 2);
64//! println!("{:?}", splitter.next().unwrap());
65//! println!("{:?}", splitter.next().unwrap());
66//! println!("{:?}", splitter.next().unwrap());
67//! ```
68
69/// Import all necessary traits and structs.
70pub mod prelude {
71    pub use crate::{SplitEvery, SplitEveryImpl, SplitEveryIterImpl};
72}
73
74pub trait SplitEveryImpl: Sized {
75    fn split_every_n_times(self, pat: Self, n: usize) -> SplitEvery<Self, Self> {
76        SplitEvery {
77            input: self,
78            pat,
79            n,
80            ind: 0,
81        }
82    }
83}
84
85impl SplitEveryImpl for &str {}
86impl SplitEveryImpl for String {}
87impl SplitEveryImpl for std::string::Drain<'_> {}
88impl<T: Clone + PartialEq> SplitEveryImpl for Vec<T> {}
89impl<T: Clone + PartialEq> SplitEveryImpl for &[T] {}
90
91pub trait SplitEveryIterImpl<'a, T: Clone + PartialEq>: Iterator<Item = T> + Sized + 'a {
92    fn split_every_n_times(
93        mut self,
94        pat: T,
95        n: usize,
96    ) -> SplitEvery<Box<dyn FnMut() -> Option<T> + 'a>, T> {
97        SplitEvery::n_times_from_fn(Box::new(move || self.next()), pat, n)
98    }
99}
100
101impl<'a, T: Clone + PartialEq, U: Iterator<Item = T> + Sized + 'a> SplitEveryIterImpl<'a, T> for U {}
102
103pub struct SplitEvery<Input, Pattern> {
104    input: Input,
105    pat: Pattern,
106    n: usize,
107    ind: usize,
108}
109
110impl<Input: FnMut() -> Option<Pattern>, Pattern: PartialEq> SplitEvery<Input, Pattern> {
111    pub fn n_times_from_fn(input: Input, pat: Pattern, n: usize) -> SplitEvery<Input, Pattern> {
112        SplitEvery {
113            input,
114            pat,
115            n,
116            ind: 0,
117        }
118    }
119}
120
121impl<Input: FnMut() -> Option<Pattern>, Pattern: PartialEq> Iterator
122    for SplitEvery<Input, Pattern>
123{
124    type Item = Vec<Pattern>;
125
126    fn next(&mut self) -> Option<Self::Item> {
127        if self.n == 0 {
128            let out: Vec<Pattern> = std::iter::repeat_with(|| (self.input)())
129                .take_while(Option::is_some)
130                .flatten()
131                .collect();
132            if out.is_empty() {
133                return None;
134            }
135            return Some(out);
136        }
137        let mut out: Vec<Pattern> = Vec::with_capacity(5);
138        'main: for ind in 0..self.n {
139            while let Some(val) = (self.input)() {
140                if val == self.pat {
141                    if ind == unsafe { self.n.unchecked_sub(1) } {
142                        break 'main;
143                    }
144                    out.push(val);
145                    continue 'main;
146                }
147                out.push(val);
148            }
149        }
150        if out.is_empty() {
151            return None;
152        }
153        Some(out)
154    }
155}
156
157impl<Pattern: AsRef<str>> Iterator for SplitEvery<&str, Pattern> {
158    type Item = String;
159
160    fn next(&mut self) -> Option<Self::Item> {
161        split_every_next_str_helper(self)
162    }
163}
164
165impl<Pattern: AsRef<str>> Iterator for SplitEvery<String, Pattern> {
166    type Item = String;
167
168    fn next(&mut self) -> Option<Self::Item> {
169        split_every_next_str_helper(self)
170    }
171}
172
173impl<Pattern: AsRef<str>> Iterator for SplitEvery<std::string::Drain<'_>, Pattern> {
174    type Item = String;
175
176    fn next(&mut self) -> Option<Self::Item> {
177        split_every_next_str_helper(self)
178    }
179}
180
181fn split_every_next_str_helper<Input: AsRef<str>, Pattern: AsRef<str>>(
182    split_every: &mut SplitEvery<Input, Pattern>,
183) -> Option<String> {
184    let input: &str = split_every.input.as_ref();
185    if split_every.ind == input.len() {
186        return None;
187    }
188    let pat: &str = split_every.pat.as_ref();
189    let iter_haystack: &str = unsafe { input.get_unchecked(split_every.ind..) };
190    let mut len: usize = 0;
191    for ind in 0..split_every.n {
192        let haystack: &str = unsafe { iter_haystack.get_unchecked(len..) };
193        if let Some(byte_ind) = haystack.find(pat) {
194            len = unsafe { len.unchecked_add(byte_ind).unchecked_add(pat.len()) };
195            continue;
196        }
197        if ind == 0 {
198            split_every.ind = input.len();
199            return Some(haystack.to_string());
200        }
201        break;
202    }
203    split_every.ind = unsafe { split_every.ind.unchecked_add(len) };
204    Some(unsafe { iter_haystack.get_unchecked(..len.unchecked_sub(pat.len())) }.to_string())
205}
206
207impl<T: Clone + PartialEq> Iterator for SplitEvery<Vec<T>, Vec<T>> {
208    type Item = Vec<T>;
209
210    fn next(&mut self) -> Option<Self::Item> {
211        let (ind, out): (usize, Option<Vec<T>>) =
212            split_every_next_arr_helper(self, &self.input, &self.pat);
213        self.ind = ind;
214        out
215    }
216}
217
218impl<T: Clone + PartialEq> Iterator for SplitEvery<&[T], &[T]> {
219    type Item = Vec<T>;
220
221    fn next(&mut self) -> Option<Self::Item> {
222        let (ind, out): (usize, Option<Vec<T>>) =
223            split_every_next_arr_helper(self, self.input, self.pat);
224        self.ind = ind;
225        out
226    }
227}
228
229fn split_every_next_arr_helper<T, U: Clone + PartialEq>(
230    split_every: &SplitEvery<T, T>,
231    input: &[U],
232    pat: &[U],
233) -> (usize, Option<Vec<U>>) {
234    if split_every.ind == input.len() {
235        return (split_every.ind, None);
236    }
237    let iter_haystack: &[U] = unsafe { input.get_unchecked(split_every.ind..) };
238    let mut len: usize = 0;
239    for ind in 0..split_every.n {
240        if len == iter_haystack.len() {
241            break;
242        }
243        if let Some((ind, _)) = unsafe { iter_haystack.get_unchecked(len..) }
244            .windows(pat.len())
245            .enumerate()
246            .find(|(_, val)| val == &pat)
247        {
248            len = unsafe { len.unchecked_add(ind).unchecked_add(pat.len()) };
249            continue;
250        }
251        if ind == 0 {
252            return (input.len(), Some(iter_haystack.to_vec()));
253        }
254        break;
255    }
256    (
257        unsafe { split_every.ind.unchecked_add(len) },
258        Some(unsafe { iter_haystack.get_unchecked(..len.unchecked_sub(pat.len())) }.to_vec()),
259    )
260}
261
262#[test]
263fn test() {
264    let mut splitter: SplitEvery<&str, &str> = "oh oh oh oh oh".split_every_n_times(" ", 2);
265    assert_eq!(splitter.next().unwrap(), "oh oh");
266    assert_eq!(splitter.next().unwrap(), "oh oh");
267    assert_eq!(splitter.next().unwrap(), "oh");
268    assert_eq!(splitter.next(), None);
269
270    let mut splitter: SplitEvery<&str, &str> = "a a a a".split_every_n_times("b", 2);
271    assert_eq!(splitter.next().unwrap(), "a a a a");
272    assert_eq!(splitter.next(), None);
273
274    let mut splitter: SplitEvery<Box<dyn FnMut() -> Option<&'static str>>, &str> = [
275        ["This", "is", "you"],
276        ["This", "is", "me"],
277        ["This", "is", "someone"],
278        ["This", "is", "them"],
279    ]
280    .iter()
281    .flatten()
282    .copied()
283    .split_every_n_times("is", 2);
284    assert_eq!(splitter.next().unwrap(), vec!["This", "is", "you", "This"]);
285    assert_eq!(
286        splitter.next().unwrap(),
287        vec!["me", "This", "is", "someone", "This"]
288    );
289    assert_eq!(splitter.next().unwrap(), vec!["them"]);
290    assert_eq!(splitter.next(), None);
291
292    let mut iter = [
293        ["This", "is", "you"],
294        ["This", "is", "me"],
295        ["This", "is", "someone"],
296        ["This", "is", "them"],
297    ]
298    .iter()
299    .flatten()
300    .copied();
301    let mut splitter: SplitEvery<Box<dyn FnMut() -> Option<&'static str>>, &str> =
302        SplitEvery::<Box<dyn FnMut() -> Option<&'static str>>, &str>::n_times_from_fn(
303            Box::new(move || iter.next()),
304            "is",
305            2,
306        );
307    assert_eq!(splitter.next().unwrap(), vec!["This", "is", "you", "This"]);
308    assert_eq!(
309        splitter.next().unwrap(),
310        vec!["me", "This", "is", "someone", "This"]
311    );
312    assert_eq!(splitter.next().unwrap(), vec!["them"]);
313    assert_eq!(splitter.next(), None);
314
315    #[allow(clippy::type_complexity)]
316    let mut splitter: SplitEvery<&[(u8, u8)], &[(u8, u8)]> = [
317        (0, 0),
318        (0, 1),
319        (0, 0), // Split
320        (0, 0),
321        (0, 0), // Split
322        (0, 1),
323        (0, 0),
324        (0, 0), // Split
325        (0, 1),
326    ]
327    .split_every_n_times(&[(0, 0)], 2);
328    assert_eq!(splitter.next().unwrap(), vec![(0, 0), (0, 1)]);
329    assert_eq!(splitter.next().unwrap(), vec![(0, 0)]);
330    assert_eq!(splitter.next().unwrap(), vec![(0, 1), (0, 0)]);
331    assert_eq!(splitter.next().unwrap(), vec![(0, 1)]);
332    assert_eq!(splitter.next(), None);
333
334    let mut splitter: SplitEvery<&str, &str> =
335        "Oh hi there I don't really know what to say".split_every_n_times(" ", 3);
336    assert_eq!(splitter.next().unwrap(), "Oh hi there");
337    assert_eq!(splitter.next().unwrap(), "I don't really");
338    assert_eq!(splitter.next().unwrap(), "know what to");
339    assert_eq!(splitter.next().unwrap(), "say");
340    assert_eq!(splitter.next(), None);
341}