1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
//! This library provides a configurable FizzBuzz implementation. //! //! # Examples //! ## FizzBuzz from 1 to 15 //! ``` //! use extended_fizzbuzz::{fizzbuzz, Matcher}; //! //! let matchers = vec![ //! Matcher::new(3, "Fizz").unwrap(), //! Matcher::new(5, "Buzz").unwrap(), //! ]; //! //! fizzbuzz(1, 15, &matchers).unwrap(); //! ``` //! //! Output: //! ```txt //! 1 //! 2 //! Fizz //! 4 //! Buzz //! Fizz //! 7 //! 8 //! Fizz //! Buzz //! 11 //! Fizz //! 13 //! 14 //! FizzBuzz //! ``` //! //! ## FizzBuzz for single numbers //! ``` //! use extended_fizzbuzz::{line, Matcher}; //! //! let matchers = vec![ //! Matcher::new(3, "Fizz").unwrap(), //! Matcher::new(5, "Buzz").unwrap(), //! ]; //! //! assert_eq!(line(6, &matchers), "Fizz".to_string()); //! assert_eq!(line(7, &matchers), "7".to_string()); //! assert_eq!(line(10, &matchers), "Buzz".to_string()); //! assert_eq!(line(15, &matchers), "FizzBuzz".to_string()); //! ``` mod matcher; pub use matcher::*; use thiserror::Error; /// Provides a configurable version of FizzBuzz. /// /// # Parameters /// With `from` and `to` you can define the number area for which to run the operation. Both of /// these values are inclusive. This means that if you specify 1 and 10, the numbers for which /// output is produced are: 1,2,3,4,5,6,7,8,9,10 /// /// With `matchers` you can provide some `matcher::Matcher`s. These are used to configure how /// numbers are substituted with words. The matchers are tested in the order of the vector. /// /// # Errors /// - Returns `FizzBuzzError::FromBiggerThanTo`, if the `from` parameters value is bigger than the /// `to` parameters value. /// /// # Example /// ``` /// use extended_fizzbuzz::{fizzbuzz, Matcher}; /// /// let matchers = vec![ /// Matcher::new(3, "Fizz").unwrap(), /// Matcher::new(5, "Buzz").unwrap(), /// ]; /// /// assert!(fizzbuzz(1, 10, &matchers).is_ok()); /// assert!(fizzbuzz(10, 1, &matchers).is_err()); /// ``` pub fn fizzbuzz(from: usize, to: usize, matchers: &Vec<Matcher>) -> Result<(), FizzBuzzError> { if from > to { return Err(FizzBuzzError::FromBiggerThanTo { from, to }); } for i in from..(to + 1) { println!("{}", line(i, matchers)); } Ok(()) } /// Provides a configurable version of FizzBuzz for a single number. /// /// # Parameters /// With `number`, you can provide the number to calculate the result for. /// /// With `matchers` you can provide some `matcher::Matcher`s. These are used to configure how /// numbers are substituted with words. The matchers are tested in the order of the vector. /// /// # Example /// ``` /// use extended_fizzbuzz::{line, Matcher}; /// /// let matchers = vec![ /// Matcher::new(3, "Fizz").unwrap(), /// Matcher::new(5, "Buzz").unwrap(), /// ]; /// /// assert_eq!(line(1, &matchers), "1".to_string()); /// assert_eq!(line(3, &matchers), "Fizz".to_string()); /// assert_eq!(line(5, &matchers), "Buzz".to_string()); /// assert_eq!(line(15, &matchers), "FizzBuzz".to_string()); /// assert_eq!(line(16, &matchers), "16".to_string()); /// ``` pub fn line(number: usize, matchers: &Vec<Matcher>) -> String { let mut out = String::new(); for m in matchers.iter() { out += m.text(number); } if out.is_empty() { out += &number.to_string(); } out } /// All errors the `fizzbuzz()` function can produce. #[derive(Error, Debug)] #[non_exhaustive] pub enum FizzBuzzError { /// The `from` parameter has a higher value than the `to` parameter. No valid range can be /// constructed. #[error("`from` value ({from}) is bigger than `to` value({to})")] FromBiggerThanTo { from: usize, to: usize }, } #[cfg(test)] mod tests { use super::*; use rand::random; #[test] fn line_normal() { let text1 = "Fizz"; let text2 = "Buzz"; // u8 is used for rng, to ensure that the result of number1 * number2 is within the // boundaries of usize and keep relatively short rng times. let mut number1: u8 = 0; let mut number2 = 0; while number1 == 0 || number1 == 1 || number1 >= (u8::MAX / 2) { number1 = random(); } while number2 < number1 || number2 % number1 == 0 { number2 = random(); } let number1 = number1.into(); let number2 = number2.into(); let matchers = vec![ Matcher::new(number1, text1).unwrap(), Matcher::new(number2, text2).unwrap(), ]; assert_eq!(line(1, &matchers), "1"); assert_eq!(line(number1, &matchers), text1.to_string()); assert_eq!(line(number2, &matchers), text2.to_string()); assert_eq!( line(number1 * number2, &matchers), format!("{}{}", text1, text2) ); assert_eq!( line((number1 * number2) + 1, &matchers), ((number1 * number2) + 1).to_string() ); } #[test] fn line_one() { let text1 = "Fizz"; let text2 = "Buzz"; let number1 = 1; let mut number2: u8 = 0; while number2 < number1 { number2 = random(); } let number1 = number1.into(); let number2 = number2.into(); let matchers = vec![ Matcher::new(number1, text1).unwrap(), Matcher::new(number2, text2).unwrap(), ]; assert_eq!(line(1, &matchers), text1.to_string()); assert_eq!(line(number2, &matchers), format!("{}{}", text1, text2)); assert_eq!(line((number1 * number2) + 1, &matchers), text1.to_string()); } }