diff options
Diffstat (limited to 'beer-song/src')
| -rw-r--r-- | beer-song/src/intersperse.rs | 52 | ||||
| -rw-r--r-- | beer-song/src/lib.rs | 20 |
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 @@ | |||
| 1 | use 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. | ||
| 7 | pub 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 | |||
| 14 | impl<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 | |||
| 25 | impl<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. | ||
| 43 | pub 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 | |||
| 52 | impl<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 @@ | |||
| 1 | use itertools::Itertools; | ||
| 2 | |||
| 3 | pub 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 | |||
| 17 | pub 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 | } | ||
