showcase_calculator/
driver.rs1use crate::core::{CalcError, CalcResult};
9
10pub trait CalculatorDriver {
39 fn enter_expression(&mut self, expr: &str) -> CalcResult<()>;
41
42 fn get_result(&self) -> String;
44
45 fn get_input(&self) -> String;
47
48 fn clear(&mut self);
50
51 fn get_history(&self) -> Vec<HistoryItem>;
53
54 fn get_jidoka_status(&self) -> Vec<String>;
56}
57
58#[derive(Debug, Clone, PartialEq, Eq)]
60pub struct HistoryItem {
61 pub expression: String,
63 pub result: String,
65}
66
67#[cfg(feature = "tui")]
69pub mod tui_driver {
70 use super::{CalcResult, CalculatorDriver, HistoryItem};
71 use crate::tui::CalculatorApp;
72
73 #[derive(Debug)]
75 pub struct TuiDriver {
76 app: CalculatorApp,
77 }
78
79 impl Default for TuiDriver {
80 fn default() -> Self {
81 Self::new()
82 }
83 }
84
85 impl TuiDriver {
86 #[must_use]
88 pub fn new() -> Self {
89 Self {
90 app: CalculatorApp::new(),
91 }
92 }
93
94 #[must_use]
96 pub fn with_app(app: CalculatorApp) -> Self {
97 Self { app }
98 }
99
100 #[must_use]
102 pub fn app(&self) -> &CalculatorApp {
103 &self.app
104 }
105
106 pub fn app_mut(&mut self) -> &mut CalculatorApp {
108 &mut self.app
109 }
110 }
111
112 impl CalculatorDriver for TuiDriver {
113 fn enter_expression(&mut self, expr: &str) -> CalcResult<()> {
114 self.app.set_input(expr);
115 self.app.evaluate();
116
117 if let Some(Err(e)) = self.app.result() {
119 return Err(e.clone());
120 }
121 Ok(())
122 }
123
124 fn get_result(&self) -> String {
125 self.app.result_display()
126 }
127
128 fn get_input(&self) -> String {
129 self.app.input().to_string()
130 }
131
132 fn clear(&mut self) {
133 self.app.clear();
134 }
135
136 fn get_history(&self) -> Vec<HistoryItem> {
137 self.app
138 .history()
139 .iter_rev()
140 .map(|entry| HistoryItem {
141 expression: entry.expression.clone(),
142 result: format!("{}", entry.result),
143 })
144 .collect()
145 }
146
147 fn get_jidoka_status(&self) -> Vec<String> {
148 self.app.jidoka_status()
149 }
150 }
151}
152
153#[cfg(feature = "tui")]
154pub use tui_driver::TuiDriver;
155
156#[allow(clippy::unwrap_used)]
161pub fn verify_basic_arithmetic<D: CalculatorDriver>(driver: &mut D) {
162 driver.enter_expression("2 + 3").unwrap();
164 assert_eq!(driver.get_result(), "5");
165 driver.clear();
166
167 driver.enter_expression("10 - 4").unwrap();
169 assert_eq!(driver.get_result(), "6");
170 driver.clear();
171
172 driver.enter_expression("6 * 7").unwrap();
174 assert_eq!(driver.get_result(), "42");
175 driver.clear();
176
177 driver.enter_expression("20 / 4").unwrap();
179 assert_eq!(driver.get_result(), "5");
180 driver.clear();
181}
182
183#[allow(clippy::unwrap_used)]
185pub fn verify_precedence<D: CalculatorDriver>(driver: &mut D) {
186 driver.enter_expression("2 + 3 * 4").unwrap();
188 assert_eq!(driver.get_result(), "14");
189 driver.clear();
190
191 driver.enter_expression("2 * 3 ^ 2").unwrap();
193 assert_eq!(driver.get_result(), "18");
194 driver.clear();
195
196 driver.enter_expression("(2 + 3) * 4").unwrap();
198 assert_eq!(driver.get_result(), "20");
199 driver.clear();
200}
201
202#[allow(clippy::unwrap_used)]
204pub fn verify_complex_expressions<D: CalculatorDriver>(driver: &mut D) {
205 driver.enter_expression("42 * (3 + 7)").unwrap();
207 assert_eq!(driver.get_result(), "420");
208 driver.clear();
209
210 driver.enter_expression("((2 + 3) * (4 + 5))").unwrap();
212 assert_eq!(driver.get_result(), "45");
213 driver.clear();
214
215 driver.enter_expression("2 ^ 3 ^ 2").unwrap();
217 assert_eq!(driver.get_result(), "512");
218 driver.clear();
219}
220
221pub fn verify_error_handling<D: CalculatorDriver>(driver: &mut D) {
223 let result = driver.enter_expression("1 / 0");
225 assert!(result.is_err());
226 assert!(matches!(result, Err(CalcError::DivisionByZero)));
227 driver.clear();
228
229 let _result = driver.enter_expression("");
231 driver.clear();
233}
234
235#[allow(clippy::unwrap_used)]
237pub fn verify_history<D: CalculatorDriver>(driver: &mut D) {
238 driver.clear();
239
240 driver.enter_expression("1 + 1").unwrap();
242 driver.enter_expression("2 + 2").unwrap();
243 driver.enter_expression("3 + 3").unwrap();
244
245 let history = driver.get_history();
246 assert!(history.len() >= 3);
247
248 assert_eq!(history[0].expression, "3 + 3");
250 assert_eq!(history[0].result, "6");
251}
252
253#[allow(clippy::unwrap_used)]
255pub fn verify_jidoka_status<D: CalculatorDriver>(driver: &mut D) {
256 driver.clear();
257
258 driver.enter_expression("5 + 5").unwrap();
260 let status = driver.get_jidoka_status();
261
262 assert!(!status.is_empty());
264 assert!(status
265 .iter()
266 .any(|s| s.contains('✓') || s.contains("satisfied")));
267}
268
269pub fn run_full_specification<D: CalculatorDriver>(driver: &mut D) {
271 verify_basic_arithmetic(driver);
272 verify_precedence(driver);
273 verify_complex_expressions(driver);
274 verify_error_handling(driver);
275 verify_history(driver);
276 verify_jidoka_status(driver);
277}
278
279#[cfg(test)]
280mod tests {
281 use super::*;
282
283 #[cfg(feature = "tui")]
286 mod tui_tests {
287 use super::*;
288
289 #[test]
290 fn test_tui_driver_new() {
291 let driver = TuiDriver::new();
292 assert!(driver.get_input().is_empty());
293 }
294
295 #[test]
296 fn test_tui_driver_default() {
297 let driver = TuiDriver::default();
298 assert!(driver.get_input().is_empty());
299 }
300
301 #[test]
302 fn test_tui_driver_with_app() {
303 let app = crate::tui::CalculatorApp::new();
304 let driver = TuiDriver::with_app(app);
305 assert!(driver.get_input().is_empty());
306 }
307
308 #[test]
309 fn test_tui_driver_app_access() {
310 let mut driver = TuiDriver::new();
311 driver.app_mut().set_input("test");
312 assert_eq!(driver.app().input(), "test");
313 }
314
315 #[test]
316 fn test_tui_driver_enter_expression() {
317 let mut driver = TuiDriver::new();
318 driver.enter_expression("2 + 2").unwrap();
319 assert_eq!(driver.get_result(), "4");
320 }
321
322 #[test]
323 fn test_tui_driver_get_input() {
324 let mut driver = TuiDriver::new();
325 driver.enter_expression("5 * 5").unwrap();
326 assert_eq!(driver.get_input(), "5 * 5");
327 }
328
329 #[test]
330 fn test_tui_driver_clear() {
331 let mut driver = TuiDriver::new();
332 driver.enter_expression("1 + 1").unwrap();
333 driver.clear();
334 assert!(driver.get_result().is_empty());
335 }
336
337 #[test]
338 fn test_tui_driver_history() {
339 let mut driver = TuiDriver::new();
340 driver.enter_expression("1 + 1").unwrap();
341 driver.enter_expression("2 + 2").unwrap();
342 let history = driver.get_history();
343 assert_eq!(history.len(), 2);
344 }
345
346 #[test]
347 fn test_tui_driver_jidoka() {
348 let mut driver = TuiDriver::new();
349 driver.enter_expression("10 + 10").unwrap();
350 let status = driver.get_jidoka_status();
351 assert!(!status.is_empty());
352 }
353
354 #[test]
355 fn test_tui_driver_error_handling() {
356 let mut driver = TuiDriver::new();
357 let result = driver.enter_expression("1 / 0");
358 assert!(matches!(result, Err(CalcError::DivisionByZero)));
359 }
360
361 #[test]
364 fn test_unified_basic_arithmetic() {
365 let mut driver = TuiDriver::new();
366 verify_basic_arithmetic(&mut driver);
367 }
368
369 #[test]
370 fn test_unified_precedence() {
371 let mut driver = TuiDriver::new();
372 verify_precedence(&mut driver);
373 }
374
375 #[test]
376 fn test_unified_complex_expressions() {
377 let mut driver = TuiDriver::new();
378 verify_complex_expressions(&mut driver);
379 }
380
381 #[test]
382 fn test_unified_error_handling() {
383 let mut driver = TuiDriver::new();
384 verify_error_handling(&mut driver);
385 }
386
387 #[test]
388 fn test_unified_history() {
389 let mut driver = TuiDriver::new();
390 verify_history(&mut driver);
391 }
392
393 #[test]
394 fn test_unified_jidoka_status() {
395 let mut driver = TuiDriver::new();
396 verify_jidoka_status(&mut driver);
397 }
398
399 #[test]
400 fn test_full_specification() {
401 let mut driver = TuiDriver::new();
402 run_full_specification(&mut driver);
403 }
404 }
405
406 #[test]
409 fn test_history_item_debug() {
410 let item = HistoryItem {
411 expression: "1+1".into(),
412 result: "2".into(),
413 };
414 assert!(format!("{:?}", item).contains("expression"));
415 }
416
417 #[test]
418 fn test_history_item_clone() {
419 let item = HistoryItem {
420 expression: "test".into(),
421 result: "42".into(),
422 };
423 let cloned = item.clone();
424 assert_eq!(item, cloned);
425 }
426}