Fizzbuzz in Rust / src / lib.rs

//! The `fizzbuzz` crate provides the way to get a customizable fizzbuzz implementation
//! 
//! # Examples
//! 
//! ```
//!    let mut acc = "".to_string();
//!    for i in 1..16 {
//!        acc = acc + &*fizzbuzz::cowbuzz(&[
//!            ("Fizz", &|i: i32| i % 3 == 0),
//!            ("Buzz", &|i: i32| i % 5 == 0),
//!        ], i) + " ";
//!    }
//! 
//!    assert_eq!(acc, "1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz ".to_string());
//! ```

extern crate monoid;
use monoid::Monoid;
use std::borrow::Cow;
use std::convert::Into;

///takes a tuple of strings and conditions
///shows the numbers if conditions are false
pub fn fizzbuzz<'a>(tuples: &[(&'a str, &Fn(i32) -> bool)], i: i32) -> String {
    let default = || i.to_string();
    accumulate(tuples, i).unwrap_or_else(default)
    //unwrap_or_else is equivalent to fromMaybe
}

///takes a tuple of strings and conditions
///shows the numbers if conditions are false
///faster than using a String
pub fn cowbuzz<'a>(tuples: &[(&'a str, &Fn(i32) -> bool)], i: i32) -> Cow<'a, str> {
    let default = || i.to_string().into();
    accumulate(tuples, i).unwrap_or_else(default)
    //unwrap_or_else is equivalent to fromMaybe
}

//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) -> Option<T> 
        where &'a str: Into<T> {
    tuples.iter().fold(None, |acc, &(concat_str, include_fn)|
        append(acc, concat_str, include_fn(i))
    )
}

//concatenates the strings if the condition is met
fn append<'a, T: Monoid>(a: Option<T>, b: &'a str, include: bool) -> Option<T>
        where &'a str: Into<T> {
    a.op( if_some(b, include) )
}

//only perform an expensive operation if the condition is true
fn if_some<T: Into<U>, U>(a: T, cond: bool) -> Option<U> {
    if cond { Some(a.into()) } else { None }
}

#[cfg(test)]
mod tests {
    use super::append;
    
    #[test]
    fn none() {
        assert_eq!(None::<String>, append(None, "b", false));
    }
    
    #[test]
    fn a() {
        assert_eq!(Some("a".to_string()), append(Some("a".to_string()), "b", false));
    }
    
    #[test]
    fn b() {
        assert_eq!(Some("b".to_string()), append(None, "b", true));
    }
    
    #[test]
    fn ab() {
        assert_eq!(Some("ab".to_string()), append(Some("a".to_string()), "b", true));
    }
}