mod store; mod ledger; use store::{Context, Store}; use miette::{Diagnostic, ErrReport, Result}; use thiserror::Error; #[derive(Default)] pub struct App { context: Context, error: Option, exit: bool, } #[derive(Diagnostic, Debug, Error, PartialEq)] #[error("An overflow error occurred")] #[diagnostic()] pub struct OverflowError { #[help] pub _msg: String, } impl App { /// Build context in App and pass it forward pub fn init_context(&mut self) -> Result<&mut Self> { let store = self.context.unwrap_store(); store.init()?; Ok(self) } pub fn get_exit_status(&self) -> bool { self.exit } pub fn exit(&mut self) -> Result<()> { self.exit = true; Ok(()) } pub fn decrement_counter(&mut self) -> Result<()> { let store = self.context.unwrap_store(); let count = store.get_counter()?; if count == 0 { Err(OverflowError { _msg: "Cannot count below 0".into(), } .into()) } else { store.set_counter(count - 1)?; Ok(()) } } pub fn increment_counter(&mut self) -> Result<()> { let store = self.context.unwrap_store(); let count = store.get_counter()?; if count == u8::MAX { Err(OverflowError { _msg: "Cannot count above u8::MAX".into(), } .into()) } else { store.set_counter(count + 1)?; Ok(()) } } pub fn display_counter(&self) -> String { self.context .get_store() .get_counter() .map(|u| u.to_string()) .unwrap_or("Error".into()) } pub fn set_error(&mut self, error: ErrReport) -> Result<()> { self.error = Some(error); Ok(()) } pub fn get_error(&self) -> &Option { &self.error } /// Clears any existing input error in the app state pub fn clear_error(&mut self) -> Result<()> { self.error = None; Ok(()) } } #[cfg(test)] mod tests { use super::*; use store::ContextError; fn get_count(app: &App) -> Result { app.context.get_store().get_counter() } #[test] fn display_counter_inited() { let mut app = App::default(); assert!(app.init_context().is_ok()); let count = app.display_counter(); assert_eq!(count, "0"); } #[test] fn display_counter_noninited() { let app = App::default(); let count = app.display_counter(); assert_eq!(count, "Error"); } #[test] fn increment_decrement() { let mut app = App::default(); assert!(app.init_context().is_ok()); let res = app.increment_counter(); assert!(res.is_ok()); assert_eq!(get_count(&app).unwrap(), 1); let res = app.decrement_counter(); assert!(res.is_ok()); assert_eq!(get_count(&app).unwrap(), 0); } #[test] fn increment_n() { let mut app = App::default(); assert!(app.init_context().is_ok()); let n = 8; for i in 1..=n { let res = app.increment_counter(); assert!(res.is_ok()); assert_eq!(get_count(&app).unwrap(), i); } assert_eq!(get_count(&app).unwrap(), n); } #[test] fn increment_n_decrement() { let mut app = App::default(); assert!(app.init_context().is_ok()); let n = 8; for i in 1..=n { let res = app.increment_counter(); assert!(res.is_ok()); assert_eq!(get_count(&app).unwrap(), i); } assert_eq!(get_count(&app).unwrap(), n); let res = app.decrement_counter(); assert!(res.is_ok()); assert_eq!(get_count(&app).unwrap(), n - 1); } #[test] fn exit() { let mut app = App::default(); assert!(app.init_context().is_ok()); let res = app.exit(); assert!(res.is_ok()); assert!(app.exit); } #[test] fn decrement_counter_error() { let mut app = App::default(); assert!(app.init_context().is_ok()); let res = app.decrement_counter(); assert!(res.is_err()); let res_err = res.unwrap_err(); let casted_val = res_err .downcast_ref::() .expect("something bad occurred"); let expected_err_msg: String = "Cannot count below 0".into(); assert_eq!(casted_val._msg, expected_err_msg); assert_eq!( *casted_val, OverflowError { _msg: expected_err_msg, }, ); } #[test] fn increment_counter_error() { let mut app = App::default(); assert!(app.init_context().is_ok()); assert!(app.context.unwrap_store().set_counter(u8::MAX).is_ok()); let res = app.increment_counter(); assert!(res.is_err()); let res_err = res.unwrap_err(); let casted_val = res_err .downcast_ref::() .expect("something bad occurred"); let expected_err_msg: String = "Cannot count above u8::MAX".into(); assert_eq!(casted_val._msg, expected_err_msg); assert_eq!( *casted_val, OverflowError { _msg: expected_err_msg, }, ); } }