1use super::{MAX_VALUE, NotationError};
4use std::fmt;
5use std::str::FromStr;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum Operator {
12 Add,
14 Sub,
16 Mul,
18 Div,
20}
21
22impl FromStr for Operator {
23 type Err = NotationError;
24
25 fn from_str(s: &str) -> Result<Self, Self::Err> {
26 let operator = match s {
27 "+" => Operator::Add,
28 "-" => Operator::Sub,
29 "*" => Operator::Mul,
30 "/" => Operator::Div,
31 _ => return Err(NotationError::InvalidOperator),
32 };
33
34 Ok(operator)
35 }
36}
37
38impl fmt::Display for Operator {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 match self {
41 Operator::Add => write!(f, "+")?,
42 Operator::Sub => write!(f, "-")?,
43 Operator::Mul => write!(f, "*")?,
44 Operator::Div => write!(f, "/")?,
45 }
46
47 Ok(())
48 }
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub struct Modifier {
56 operator: Operator,
57 value: u32,
58}
59
60impl Modifier {
61 pub fn try_new(operator: Operator, value: u32) -> Result<Self, NotationError> {
80 match (operator, value) {
81 (Operator::Div, 0) => Err(NotationError::Zero("divisor")),
82 (_, 0..=MAX_VALUE) => Ok(Modifier { operator, value }),
83 _ => Err(NotationError::NumberTooLarge(MAX_VALUE)),
84 }
85 }
86
87 #[must_use]
89 pub fn operator(self) -> Operator {
90 self.operator
91 }
92
93 #[must_use]
95 pub fn value(self) -> u32 {
96 self.value
97 }
98
99 #[must_use]
117 pub fn apply(self, total: u32) -> u32 {
118 match self.operator {
119 Operator::Add => total.saturating_add(self.value),
120 Operator::Sub => total.saturating_sub(self.value),
121 Operator::Mul => total.saturating_mul(self.value),
122 Operator::Div => total / self.value,
123 }
124 }
125}
126
127impl fmt::Display for Modifier {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 write!(f, "{}{}", self.operator, self.value)
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use rstest::rstest;
136
137 use super::*;
138
139 mod operator {
140 use super::*;
141
142 mod from_str {
143 use super::*;
144
145 #[rstest]
146 #[case::add("+", Operator::Add)]
147 #[case::sub("-", Operator::Sub)]
148 #[case::mul("*", Operator::Mul)]
149 #[case::div("/", Operator::Div)]
150 fn accepts(#[case] input: &str, #[case] expected: Operator) {
151 let result: Operator = input.parse().unwrap();
152
153 assert_eq!(result, expected);
154 }
155
156 #[rstest]
157 #[case::empty("")]
158 #[case::one_char_string("%")]
159 #[case::duplicated_operator("++")]
160 fn rejects(#[case] input: &str) {
161 let result = input.parse::<Operator>();
162
163 assert!(matches!(result, Err(NotationError::InvalidOperator)));
164 }
165 }
166
167 mod display {
168 use super::*;
169
170 #[rstest]
171 #[case::add(Operator::Add, String::from("+"))]
172 #[case::sub(Operator::Sub, String::from("-"))]
173 #[case::mul(Operator::Mul, String::from("*"))]
174 #[case::div(Operator::Div, String::from("/"))]
175 fn returns_formatted_string(#[case] operator: Operator, #[case] expected: String) {
176 assert_eq!(operator.to_string(), expected);
177 }
178 }
179 }
180
181 mod modifier {
182 use super::*;
183
184 mod display {
185 use super::*;
186
187 #[test]
188 fn returns_formatted_string() {
189 let modifier = Modifier {
190 operator: Operator::Add,
191 value: 10,
192 };
193
194 let result = modifier.to_string();
195
196 assert_eq!(result, "+10");
197 }
198 }
199
200 mod try_new {
201 use super::*;
202
203 #[rstest]
204 #[case::add(Operator::Add)]
205 #[case::sub(Operator::Sub)]
206 #[case::mul(Operator::Mul)]
207 fn accepts_zero_for_non_division_operators(#[case] operator: Operator) {
208 let value = 0;
209
210 let result = Modifier::try_new(operator, value);
211
212 assert_eq!(result, Ok(Modifier { operator, value }));
213 }
214
215 #[rstest]
216 #[case::add(Operator::Add)]
217 #[case::sub(Operator::Sub)]
218 #[case::mul(Operator::Mul)]
219 #[case::div(Operator::Div)]
220 fn accepts_maximum_value(#[case] operator: Operator) {
221 let value = MAX_VALUE;
222
223 let result = Modifier::try_new(operator, value);
224
225 assert_eq!(result, Ok(Modifier { operator, value }));
226 }
227
228 #[rstest]
229 #[case::add(Operator::Add)]
230 #[case::sub(Operator::Sub)]
231 #[case::mul(Operator::Mul)]
232 #[case::div(Operator::Div)]
233 fn rejects_above_the_maximum(#[case] operator: Operator) {
234 let value = MAX_VALUE + 1;
235
236 let result = Modifier::try_new(operator, value);
237
238 assert!(matches!(
239 result,
240 Err(NotationError::NumberTooLarge(MAX_VALUE))
241 ));
242 }
243
244 #[test]
245 fn rejects_division_by_zero() {
246 let operator = Operator::Div;
247 let value = 0;
248
249 let result = Modifier::try_new(operator, value);
250
251 assert!(matches!(result, Err(NotationError::Zero("divisor"))));
252 }
253 }
254
255 mod apply {
256 use super::*;
257
258 #[rstest]
259 #[case::add(4, Operator::Add, 2, 6)]
260 #[case::add_saturates(u32::MAX, Operator::Add, 1, u32::MAX)]
261 #[case::sub(4, Operator::Sub, 2, 2)]
262 #[case::sub_saturates(1, Operator::Sub, 2, u32::MIN)]
263 #[case::mul(4, Operator::Mul, 3, 12)]
264 #[case::mul_saturates(4, Operator::Mul, u32::MAX / 2 + 1, u32::MAX)]
265 #[case::div(8, Operator::Div, 2, 4)]
266 #[case::div_rounds_down(7, Operator::Div, 2, 3)]
267 fn returns_expected_value(
268 #[case] total: u32,
269 #[case] operator: Operator,
270 #[case] value: u32,
271 #[case] expected: u32,
272 ) {
273 let modifier = Modifier { operator, value };
274
275 let result = modifier.apply(total);
276
277 assert_eq!(result, expected);
278 }
279 }
280 }
281}