aboutsummaryrefslogtreecommitdiff
path: root/beer-song/src
diff options
context:
space:
mode:
Diffstat (limited to 'beer-song/src')
-rw-r--r--beer-song/src/intersperse.rs52
-rw-r--r--beer-song/src/lib.rs20
2 files changed, 72 insertions, 0 deletions
diff --git a/beer-song/src/intersperse.rs b/beer-song/src/intersperse.rs
new file mode 100644
index 0000000..51580e6
--- /dev/null
+++ b/beer-song/src/intersperse.rs
@@ -0,0 +1,52 @@
1use std::rc::Rc;
2
3/// An iterator that allows to insert a given element `elem` every `interval` elements of the input
4/// iterator.
5///
6/// The interleaved element is wrapped in a `Rc` smart pointer to avoid hard copies.
7pub struct Intersperse<I: Iterator> {
8 inner: std::iter::Peekable<I>, // required to have `peek(..)`
9 elem: Rc<I::Item>,
10 interval: u32,
11 intersperse: u32,
12}
13
14impl<I: Iterator> Intersperse<I> {
15 pub fn new(inner: I, elem: I::Item, interval: u32) -> Intersperse<I> {
16 Intersperse {
17 inner: inner.peekable(),
18 elem: Rc::new(elem),
19 interval: interval + 1, // `+ 1` needed to align semantics and implementation
20 intersperse: 0,
21 }
22 }
23}
24
25impl<I: Iterator> Iterator for Intersperse<I> {
26 type Item = Rc<I::Item>;
27
28 fn next(&mut self) -> Option<Self::Item> {
29 // Counter rotation
30 self.intersperse = (self.intersperse + 1) % self.interval;
31 // Insert a copy of the element only when counter goes to 0 and we haven't reached the end
32 // of the inner iterator.
33 if self.intersperse == 0 && self.inner.peek().is_some() {
34 Some(Rc::clone(&self.elem))
35 } else {
36 self.inner.next()
37 .map(|x| Rc::new(x)) // Go from Option<I::Item> to Option<Rc<I::Item>>
38 }
39 }
40}
41
42/// Extend `Iterator`s with `intersperse` function.
43pub trait IntersperseIterator: Iterator {
44 fn intersperse(self, elem: Self::Item, interval: u32) -> Intersperse<Self>
45 where
46 Self: Sized,
47 {
48 Intersperse::new(self, elem, interval)
49 }
50}
51
52impl<I: Iterator> IntersperseIterator for I {}
diff --git a/beer-song/src/lib.rs b/beer-song/src/lib.rs
new file mode 100644
index 0000000..990fbbf
--- /dev/null
+++ b/beer-song/src/lib.rs
@@ -0,0 +1,20 @@
1use itertools::Itertools;
2
3pub fn verse(n: u32) -> String {
4 match n {
5 0 => format!( "No more bottles of beer on the wall, no more bottles of beer.\n\
6 Go to the store and buy some more, 99 bottles of beer on the wall.\n"),
7 1 => format!( "1 bottle of beer on the wall, 1 bottle of beer.\n\
8 Take it down and pass it around, no more bottles of beer on the wall.\n"),
9 2 => format!( "2 bottles of beer on the wall, 2 bottles of beer.\n\
10 Take one down and pass it around, 1 bottle of beer on the wall.\n"),
11 _ => format!( "{} bottles of beer on the wall, {} bottles of beer.\n\
12 Take one down and pass it around, {} bottles of beer on the wall.\n",
13 n, n, n - 1)
14 }
15}
16
17pub fn sing(start: u32, end: u32) -> String {
18 // Note: call to `join` can be substituted with `.intersperse(String::from("\n")).collect()`
19 (end..=start).rev().map(|x| verse(x)).join("\n")
20}