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}