aboutsummaryrefslogtreecommitdiff
path: root/rust/beer-song/src/intersperse.rs
blob: 51580e63ec958f4df4494ce7106ecec927e80cf0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
use std::rc::Rc;

/// An iterator that allows to insert a given element `elem` every `interval` elements of the input
/// iterator.
///
/// The interleaved element is wrapped in a `Rc` smart pointer to avoid hard copies.
pub struct Intersperse<I: Iterator> {
    inner: std::iter::Peekable<I>, // required to have `peek(..)`
    elem: Rc<I::Item>,
    interval: u32,
    intersperse: u32,
}

impl<I: Iterator> Intersperse<I> {
    pub fn new(inner: I, elem: I::Item, interval: u32) -> Intersperse<I> {
        Intersperse {
            inner: inner.peekable(),
            elem: Rc::new(elem),
            interval: interval + 1, // `+ 1` needed to align semantics and implementation
            intersperse: 0,
        }
    }
}

impl<I: Iterator> Iterator for Intersperse<I> {
    type Item = Rc<I::Item>;

    fn next(&mut self) -> Option<Self::Item> {
        // Counter rotation
        self.intersperse = (self.intersperse + 1) % self.interval;
        // Insert a copy of the element only when counter goes to 0 and we haven't reached the end
        // of the inner iterator.
        if self.intersperse == 0 && self.inner.peek().is_some() {
            Some(Rc::clone(&self.elem))
        } else {
            self.inner.next()
                .map(|x| Rc::new(x)) // Go from Option<I::Item> to Option<Rc<I::Item>>
        }
    }
}

/// Extend `Iterator`s with `intersperse` function.
pub trait IntersperseIterator: Iterator {
    fn intersperse(self, elem: Self::Item, interval: u32) -> Intersperse<Self>
    where
        Self: Sized,
    {
        Intersperse::new(self, elem, interval)
    }
}

impl<I: Iterator> IntersperseIterator for I {}