use std::iter::Iterator;
pub trait IterChunks: Sized + Iterator {
fn chunks(self, n: usize) -> Chunks<Self>;
}
impl<I> IterChunks for I
where
I: Iterator,
{
fn chunks(self, n: usize) -> Chunks<Self> {
assert_ne!(n, 0);
Chunks {
inner: self,
n,
end_flag: false,
}
}
}
pub struct Chunks<I: Iterator> {
inner: I,
n: usize,
end_flag: bool,
}
impl<I: Iterator> Chunks<I> {
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Option<Chunk<'_, I>> {
if self.end_flag {
self.end_flag = false;
None
} else {
match self.inner.next() {
Some(v) => {
let n = self.n;
Some(Chunk {
first: Some(v),
parent: self,
n: n - 1,
})
}
None => None,
}
}
}
pub fn for_each(&mut self, mut f: impl FnMut(Chunk<'_, I>)) {
while let Some(item) = self.next() {
f(item)
}
}
}
pub struct Chunk<'a, I: Iterator> {
first: Option<I::Item>,
parent: &'a mut Chunks<I>,
n: usize,
}
impl<'a, I> Iterator for Chunk<'a, I>
where
I: Iterator,
{
type Item = <I as Iterator>::Item;
fn next(&mut self) -> Option<Self::Item> {
match self.first.take() {
Some(v) => Some(v),
None if self.n > 0 => {
self.n -= 1;
match self.parent.inner.next() {
Some(v) => Some(v),
None => {
self.n = 0;
self.parent.end_flag = true;
None
}
}
}
None => None,
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (lower, upper) = self.parent.inner.size_hint();
let has_first = self.first.is_some() as usize;
let n = self.n;
let lower = lower.min(n) + has_first;
let upper = upper.map(|v| v.min(n) + has_first);
(lower, upper)
}
}
#[cfg(test)]
mod tests {
use super::IterChunks;
#[test]
fn test_impls() {
let chunks = [0i32].into_iter().chunks(1);
fn assert_send<T: Send>(_: &T) {}
fn assert_sync<T: Sync>(_: &T) {}
assert_sync(&chunks);
assert_send(&chunks);
}
#[test]
fn test_chunks() {
let arr = [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3];
let mut i = 0;
let mut chunks = arr.into_iter().chunks(3);
while let Some(chunk) = chunks.next() {
for v in chunk {
assert_eq!(v, i);
}
i += 1;
}
assert_eq!(i, 4);
}
#[test]
fn test_chunk_resumable() {
let inner_gen = |rem| {
let mut i = 0;
std::iter::from_fn(move || {
i += 1;
if i % rem == 0 {
None
} else {
Some(i)
}
})
};
let inner = inner_gen(3);
let mut chunks = inner.chunks(4);
while let Some(chunk) = chunks.next() {
assert_eq!(chunk.collect::<Vec<_>>(), vec![1, 2]);
}
while let Some(chunk) = chunks.next() {
assert_eq!(chunk.collect::<Vec<_>>(), vec![4, 5]);
}
while let Some(chunk) = chunks.next() {
assert_eq!(chunk.collect::<Vec<_>>(), vec![7, 8]);
}
let inner = inner_gen(6);
let mut chunks = inner.chunks(4);
assert_eq!(chunks.next().unwrap().collect::<Vec<_>>(), vec![1, 2, 3, 4]);
assert_eq!(chunks.next().unwrap().collect::<Vec<_>>(), vec![5]);
assert!(chunks.next().is_none());
assert_eq!(
chunks.next().unwrap().collect::<Vec<_>>(),
vec![7, 8, 9, 10]
);
assert_eq!(chunks.next().unwrap().collect::<Vec<_>>(), vec![11]);
assert!(chunks.next().is_none());
}
#[test]
fn test_chunks_count() {
let arr: [bool; 0] = [];
let mut i = 0;
let mut chunks = arr.into_iter().chunks(3);
while let Some(chunk) = chunks.next() {
for _ in chunk {}
i += 1;
}
assert_eq!(i, 0);
let arr: [bool; 3] = [false; 3];
let mut i = 0;
let mut chunks = arr.into_iter().chunks(3);
while let Some(chunk) = chunks.next() {
for _ in chunk {}
i += 1;
}
assert_eq!(i, 1);
}
#[test]
fn test_size_hint() {
let iter = [1, 2, 3, 4]
.into_iter()
.chain([5, 6, 7].into_iter().filter(|_| true));
let (lower, upper) = iter.size_hint();
assert_eq!(lower, 4);
assert_eq!(upper, Some(7));
let mut chunks = iter.chunks(3);
let mut chunk1 = chunks.next().unwrap();
assert_eq!(chunk1.size_hint(), (3, Some(3)));
chunk1.next().unwrap();
assert_eq!(chunk1.size_hint(), (2, Some(2)));
for _ in chunk1 {}
let chunk2 = chunks.next().unwrap();
assert_eq!(chunk2.size_hint(), (1, Some(3)));
for _ in chunk2 {}
let mut chunk3 = chunks.next().unwrap();
assert_eq!(chunk3.size_hint(), (1, Some(1)));
chunk3.next().unwrap();
assert_eq!(chunk3.size_hint(), (0, Some(0)));
}
}