over_engineered_fizzbuzz/
fizzbuzz.rs

1use std::{
2    fmt::Debug,
3    ops::{Add, AddAssign},
4};
5
6#[derive(Clone)]
7struct Param {
8    pub string: String,
9    pub value: i32,
10}
11
12impl Param {
13    pub fn new(string: &str, value: i32) -> Param {
14        Self {
15            string: string.to_string(),
16            value,
17        }
18    }
19
20    pub fn tuple(self) -> (String, i32) {
21        (self.string, self.value)
22    }
23}
24
25impl From<(&str, i32)> for Param {
26    fn from(value: (&str, i32)) -> Self {
27        Self::new(value.0, value.1)
28    }
29}
30
31impl Debug for Param {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        write!(f, "{:?}", (&self.string, self.value))
34    }
35}
36
37/// An over-engineered [FizzBuzz] implementation.
38/// # Usage
39/// - Basic implementation
40/// ```
41/// # use over_engineered_fizzbuzz::*;
42/// let fb = FizzBuzz::default();
43///
44/// for i in 1..100 {
45///     let res = fb.compute(i);
46///     println!("{res}");
47/// }
48/// ```
49/// - Add Paramater
50/// ```
51/// # use over_engineered_fizzbuzz::*;
52/// let mut fb = FizzBuzz::default();
53///
54/// fb = fb + ("Kazz", 7);
55/// fb += ("Vezz", 11);
56///
57/// let left = fb.compute(3 * 5 * 7 * 11);
58/// let right = "FizzBuzzKazzVezz";
59///
60/// assert_eq!(left, right);
61/// ```
62#[derive(Clone, Debug)]
63pub struct FizzBuzz {
64    params: Vec<Param>,
65}
66
67impl FizzBuzz {
68    /// Checks to see if the given input is divisible by any of its stored paramaters.
69    /// # Usage
70    /// ```
71    /// # use over_engineered_fizzbuzz::*;
72    /// let fb = FizzBuzz::default();
73    /// assert_eq!(fb.compute(15), "FizzBuzz");
74    /// ```
75    pub fn compute(&self, a: i32) -> String {
76        compute_fizzbuzz(self, a)
77    }
78
79    /// Perfoms [compute_fizzbuzz] on all items in an iterator
80    /// # Usage
81    /// ```
82    /// # use over_engineered_fizzbuzz::*;
83    /// let left = FizzBuzz::default().compute_iter(1..=5);
84    /// let right = ["1", "2", "Fizz", "4", "Buzz"];
85    /// assert_eq!(left, right);
86    /// ```
87    pub fn compute_iter<I: Iterator<Item = i32>>(&self, iter: I) -> Vec<String> {
88        iter.map(|x| self.compute(x)).collect()
89    }
90
91    /// Returns a [FizzBuzz] struct with `0` paramaters
92    /// # Usage
93    /// ```
94    /// # use over_engineered_fizzbuzz::*;
95    /// let left = FizzBuzz::empty().params();
96    /// let right = [];
97    ///
98    /// assert_eq!(left, right);
99    /// ```
100    pub fn empty() -> Self {
101        FizzBuzz::from(vec![])
102    }
103
104    /// Returns the [Vec] of stored paramaters
105    /// # Usage
106    /// ```
107    /// # use over_engineered_fizzbuzz::*;
108    /// let left = FizzBuzz::default().params();
109    /// let right = [("Fizz".to_string(), 3), ("Buzz".to_string(), 5)];
110    /// assert_eq!(left, right);
111    /// ```
112    pub fn params(&self) -> Vec<(String, i32)> {
113        self.params.iter().map(|p| p.clone().tuple()).collect()
114    }
115}
116
117impl Default for FizzBuzz {
118    /// The [Default] paramaters are set as the paramaters given in the original interview question.
119    ///
120    /// Fizz maps to `3`, and Buzz maps to `5`.
121    /// # Usage
122    /// ```
123    /// # use over_engineered_fizzbuzz::*;
124    /// let left = FizzBuzz::default().params();
125    /// let right = [("Fizz".to_string(), 3), ("Buzz".to_string(), 5)];
126    /// assert_eq!(left, right);
127    /// ```
128    fn default() -> Self {
129        Self::from(vec![("Fizz", 3), ("Buzz", 5)])
130    }
131}
132
133impl From<Vec<(&str, i32)>> for FizzBuzz {
134    /// # Usage
135    /// ```
136    /// # use over_engineered_fizzbuzz::*;
137    /// let fb = FizzBuzz::from(vec![("Fizz", 3), ("Buzz", 5)]);
138    /// ```
139    fn from(value: Vec<(&str, i32)>) -> Self {
140        Self {
141            params: value.into_iter().map(|(s, x)| Param::new(s, x)).collect(),
142        }
143    }
144}
145
146impl AddAssign<Param> for FizzBuzz {
147    fn add_assign(&mut self, rhs: Param) {
148        self.params.push(rhs);
149    }
150}
151
152impl AddAssign<(&str, i32)> for FizzBuzz {
153    /// # Add Paramater
154    /// ```
155    /// # use over_engineered_fizzbuzz::*;
156    /// let mut fb = FizzBuzz::default();
157    ///
158    /// fb = fb + ("Kazz", 7);
159    /// fb += ("Vezz", 11);
160    ///
161    /// let left = fb.compute(3 * 5 * 7 * 11);
162    /// let right = "FizzBuzzKazzVezz";
163    ///
164    /// assert_eq!(left, right);
165    /// ```
166    fn add_assign(&mut self, rhs: (&str, i32)) {
167        self.params.push(Param::from(rhs));
168    }
169}
170
171impl Add<Param> for FizzBuzz {
172    type Output = Self;
173    fn add(self, rhs: Param) -> Self::Output {
174        let mut clone = self.clone();
175        clone += rhs;
176        self
177    }
178}
179
180impl Add<(&str, i32)> for FizzBuzz {
181    type Output = Self;
182    /// # Add Paramater
183    /// ```
184    /// # use over_engineered_fizzbuzz::*;
185    /// let mut fb = FizzBuzz::default();
186    ///
187    /// fb = fb + ("Kazz", 7);
188    /// fb += ("Vezz", 11);
189    ///
190    /// let left = fb.compute(3 * 5 * 7 * 11);
191    /// let right = "FizzBuzzKazzVezz";
192    ///
193    /// assert_eq!(left, right);
194    /// ```
195    fn add(self, rhs: (&str, i32)) -> Self::Output {
196        let mut clone = self;
197        clone += rhs;
198        clone
199    }
200}
201
202/// Turn any [i32] [Iterator] into a [FizzBuzzIter]
203pub trait FizzBuzzed<I> {
204    /// # Usage
205    /// ```
206    /// # use over_engineered_fizzbuzz::*;
207    /// let left = (1..=5).fizzbuzz().collect::<Vec<String>>();
208    /// let right = ["1", "2", "Fizz", "4", "Buzz"];
209    ///
210    /// assert_eq!(left, right);
211    /// ```
212    fn fizzbuzz(self) -> FizzBuzzIter<I>;
213
214    /// # Usage
215    /// ```
216    /// # use over_engineered_fizzbuzz::*;
217    /// let fb = FizzBuzz::from(vec![("Carl", 3), ("Wheezer", 4)]);
218    ///
219    /// let left = (1..=5).fizzbuzz_custom(fb).collect::<Vec<String>>();
220    /// let right = ["1", "2", "Carl", "Wheezer", "5"];
221    ///
222    /// assert_eq!(left, right);
223    /// ```
224    fn fizzbuzz_custom(self, fb: FizzBuzz) -> FizzBuzzIter<I>;
225}
226
227impl<I> FizzBuzzed<I> for I
228where
229    I: Iterator<Item = i32>,
230{
231    fn fizzbuzz(self) -> FizzBuzzIter<I> {
232        self.fizzbuzz_custom(Default::default())
233    }
234
235    fn fizzbuzz_custom(self, fb: FizzBuzz) -> FizzBuzzIter<I> {
236        FizzBuzzIter::new(fb, self)
237    }
238}
239
240/// Given a [FizzBuzz] (`fb`) and an [i32] (`var`), returns a [String] of all paramaters in `fb` where `var` is divisible by them.
241///
242/// If `var` is not divisible by any of the paramaters in `fb`, will return `var.to_string()`
243/// # Usage
244/// ```
245/// # use over_engineered_fizzbuzz::*;
246/// let fb = FizzBuzz::default();
247///
248/// assert_eq!(compute_fizzbuzz(&fb, 1), "1");
249/// assert_eq!(compute_fizzbuzz(&fb, 2), "2");
250/// assert_eq!(compute_fizzbuzz(&fb, 3), "Fizz");
251/// assert_eq!(compute_fizzbuzz(&fb, 5), "Buzz");
252/// assert_eq!(compute_fizzbuzz(&fb, 15), "FizzBuzz");
253/// ```
254pub fn compute_fizzbuzz(fb: &FizzBuzz, var: i32) -> String {
255    // le painting
256    let mut canvas = String::new();
257
258    for Param { string, value } in &fb.params {
259        if var % value == 0 {
260            canvas += string;
261        }
262    }
263
264    if canvas.is_empty() {
265        var.to_string()
266    } else {
267        canvas
268    }
269}
270
271/// An [Iterator] for generating [FizzBuzz] values from any [i32] [Iterator]
272/// # Usage
273/// ```
274/// # use over_engineered_fizzbuzz::*;
275/// let fbi = FizzBuzzIter::new(FizzBuzz::default(), 1..=5);
276///
277/// let left = fbi.collect::<Vec<String>>();
278/// let right = ["1", "2", "Fizz", "4", "Buzz"];
279///
280/// assert_eq!(left, right);
281/// ```
282#[must_use = "iterators are lazy and do nothing unless consumed"]
283#[derive(Clone, Default, Debug)]
284pub struct FizzBuzzIter<I> {
285    fb: FizzBuzz,
286    iter: I,
287}
288
289impl<I: Iterator<Item = i32>> FizzBuzzIter<I> {
290    /// # Usage
291    /// ```
292    /// # use over_engineered_fizzbuzz::*;
293    /// let fbi = FizzBuzzIter::new(FizzBuzz::default(), 1..=5);
294    ///
295    /// let left = fbi.collect::<Vec<String>>();
296    /// let right = ["1", "2", "Fizz", "4", "Buzz"];
297    ///
298    /// assert_eq!(left, right);
299    /// ```
300    pub fn new(fb: FizzBuzz, iter: I) -> FizzBuzzIter<I> {
301        FizzBuzzIter { fb, iter }
302    }
303}
304
305impl<I: Iterator<Item = i32>> Iterator for FizzBuzzIter<I> {
306    type Item = String;
307
308    fn next(&mut self) -> Option<Self::Item> {
309        self.iter.next().map(|x| self.fb.compute(x))
310    }
311}