//! The `fizzbuzz` crate provides the way to get a customizable fizzbuzz implementation
//!
//! # Examples
//!
//! ```
//! let acc = (1..=15).map(|i| fizzbuzz::fizzbuzz(&[
//! ("Fizz", &|i: i32| i % 3 == 0),
//! ("Buzz", &|i: i32| i % 5 == 0),
//! ], i)).collect::<Vec<_>>().join(" ");
//!
//! assert_eq!(acc, "1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz".to_string());
//! ```
extern crate itertools;
extern crate monoid;
extern crate tool;
use itertools::Itertools;
use monoid::Monoid;
use tool::prelude::*;
use std::borrow::Cow;
use std::convert::Into;
///Takes a tuple of strings and conditions.<br>
///Shows the number if the conditions are false.
pub fn fizzbuzz<'a>(tuples: &[(&'a str, &Fn(i32) -> bool)], i: i32) -> String {
accumulate(tuples, i)
}
///Takes a tuple of strings and conditions.<br>
///Shows the number if the conditions are false.<br>
///Faster than returning a String.
pub fn cowbuzz<'a>(tuples: &[(&'a str, &Fn(i32) -> bool)], i: i32) -> Cow<'a, str> {
accumulate(tuples, i)
}
//does the monoid operation on the slice of tuples if the closure evaluates to true
fn accumulate<'a, T: Monoid>(tuples: &[(&'a str, &Fn(i32) -> bool)], i: i32) -> T
where
T: From<&'a str> + From<String>,
{
tuples
.iter()
.filter(apply(second, i))
.map(first)
.cloned()
.map(<&str>::into)
.fold1(T::op)
.unwrap_or_else(|| i.to_string().into())
//op just concatenates, but String does not satisfy Add
}
fn apply<A, B, C, G>(mut f: impl FnMut(B) -> G, a: A) -> impl FnMut(&B) -> C
// must still be `for<'r> impl FnMut(&'r B) -> C`, because that’s what filter requires
where
G: FnMut(A) -> C,
B: Copy, // for dereferencing
A: Clone,
{
move |b| f(*b)(a.clone()) // this must do any bridging necessary to satisfy the requirements
}