Fizzbuzz in Rust / src / lib.rs

//! 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
}