aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/rust/app.rs205
-rw-r--r--src/main/rust/main.rs178
2 files changed, 256 insertions, 127 deletions
diff --git a/src/main/rust/app.rs b/src/main/rust/app.rs
new file mode 100644
index 0000000..d6e84e1
--- /dev/null
+++ b/src/main/rust/app.rs
@@ -0,0 +1,205 @@
+use crate::store::{Context, Store};
+use miette::{Diagnostic, Result};
+use thiserror::Error;
+
+#[derive(Default)]
+pub struct App {
+ context: Context,
+ 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())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::store::ContextError;
+
+ fn get_count(app: &App) -> Result<u8, ContextError> {
+ 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::<OverflowError>()
+ .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::<OverflowError>()
+ .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,
+ },
+ );
+ }
+}
diff --git a/src/main/rust/main.rs b/src/main/rust/main.rs
index 2b77a18..e42fde9 100644
--- a/src/main/rust/main.rs
+++ b/src/main/rust/main.rs
@@ -1,7 +1,9 @@
+mod app;
pub mod store;
+use app::App;
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind};
-use miette::{Diagnostic, IntoDiagnostic, Result};
+use miette::{IntoDiagnostic, Result};
use ratatui::{
DefaultTerminal, Frame,
buffer::Buffer,
@@ -11,41 +13,27 @@ use ratatui::{
text::{Line, Text},
widgets::{Block, Paragraph, Widget},
};
-use store::{Context, Store};
-use thiserror::Error;
-
-#[derive(Default)]
-pub struct App {
- context: Context,
- exit: bool,
-}
-
-#[derive(Diagnostic, Debug, Error, PartialEq)]
-#[error("An overflow error occurred")]
-#[diagnostic()]
-pub struct OverflowError {
- #[help]
- _msg: String,
-}
#[cfg(not(tarpaulin_include))]
fn main() -> Result<()> {
- ratatui::run(|term| App::default().init_context()?.run(term))
+ ratatui::run(|term| {
+ let mut ctx = Runtime {
+ app: App::default(),
+ };
+ ctx.app.init_context()?;
+ ctx.run(term)
+ })
}
-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)
- }
+struct Runtime {
+ app: App,
+}
+impl Runtime {
/// Run the app's main loop until the user quits
#[cfg(not(tarpaulin_include))]
pub fn run(&mut self, terminal: &mut DefaultTerminal) -> Result<()> {
- while !self.exit {
+ while !self.app.get_exit_status() {
terminal.draw(|frame| self.draw(frame)).into_diagnostic()?;
self.handle_events()?;
}
@@ -72,59 +60,16 @@ impl App {
/// Process user input and run associated app functions
fn handle_key_event(&mut self, key_event: KeyEvent) -> Result<()> {
match key_event.code {
- KeyCode::Char('q') => self.exit()?,
- KeyCode::Left => self.decrement_counter()?,
- KeyCode::Right => self.increment_counter()?,
+ KeyCode::Char('q') => self.app.exit()?,
+ KeyCode::Left => self.app.decrement_counter()?,
+ KeyCode::Right => self.app.increment_counter()?,
_ => {}
};
Ok(())
}
-
- fn exit(&mut self) -> Result<()> {
- self.exit = true;
- Ok(())
- }
-
- 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(())
- }
- }
-
- 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(())
- }
- }
-
- fn display_counter(&self) -> String {
- self.context
- .get_store()
- .get_counter()
- .map(|u| u.to_string())
- .unwrap_or("Error".into())
- }
}
-impl Widget for &App {
+impl Widget for &Runtime {
fn render(self, area: Rect, buf: &mut Buffer) {
let title = Line::from(" Counter App Tutorial ".bold());
let instructions = Line::from(vec![
@@ -142,7 +87,7 @@ impl Widget for &App {
let counter_text = Text::from(vec![Line::from(vec![
"Value: ".into(),
- self.display_counter().yellow(),
+ self.app.display_counter().yellow(),
])]);
Paragraph::new(counter_text)
@@ -154,42 +99,24 @@ impl Widget for &App {
#[cfg(test)]
mod tests {
- use crate::store::ContextError;
use super::*;
+ use app::OverflowError;
use ratatui::style::Style;
- fn get_count(app: &App) -> Result<u8, ContextError> {
- app.context.get_store().get_counter()
- }
-
- #[test]
- fn display_counter_inited() {
+ fn default_runtime() -> Runtime {
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");
+ Runtime { app }
}
#[test]
fn render() {
- let mut app = App::default();
- assert!(app.init_context().is_ok());
+ let ctx = default_runtime();
let mut buf = Buffer::empty(Rect::new(0, 0, 50, 4));
- app.render(buf.area, &mut buf);
+ ctx.render(buf.area, &mut buf);
let mut expected = Buffer::with_lines(vec![
"┏━━━━━━━━━━━━━ Counter App Tutorial ━━━━━━━━━━━━━┓",
@@ -212,64 +139,59 @@ mod tests {
#[test]
fn handle_key_event_inc_dec() {
- let mut app = App::default();
- assert!(app.init_context().is_ok());
+ let mut ctx = default_runtime();
- let res = app.handle_key_event(KeyCode::Right.into());
+ let res = ctx.handle_key_event(KeyCode::Right.into());
assert!(res.is_ok());
- assert_eq!(get_count(&app).unwrap(), 1);
+ assert_eq!(ctx.app.display_counter(), "1");
- let res = app.handle_key_event(KeyCode::Left.into());
+ let res = ctx.handle_key_event(KeyCode::Left.into());
assert!(res.is_ok());
- assert_eq!(get_count(&app).unwrap(), 0);
+ assert_eq!(ctx.app.display_counter(), "0");
}
#[test]
fn handle_key_event_inc_n() {
- let mut app = App::default();
- assert!(app.init_context().is_ok());
+ let mut ctx = default_runtime();
let n = 8;
for i in 1..=n {
- let res = app.handle_key_event(KeyCode::Right.into());
+ let res = ctx.handle_key_event(KeyCode::Right.into());
assert!(res.is_ok());
- assert_eq!(get_count(&app).unwrap(), i);
+ assert_eq!(ctx.app.display_counter(), i.to_string());
}
- assert_eq!(get_count(&app).unwrap(), n);
+ assert_eq!(ctx.app.display_counter(), n.to_string());
}
#[test]
fn handle_key_event_inc_n_dec() {
- let mut app = App::default();
- assert!(app.init_context().is_ok());
+ let mut ctx = default_runtime();
let n = 8;
for i in 1..=n {
- let res = app.handle_key_event(KeyCode::Right.into());
+ let res = ctx.handle_key_event(KeyCode::Right.into());
assert!(res.is_ok());
- assert_eq!(get_count(&app).unwrap(), i);
+ assert_eq!(ctx.app.display_counter(), i.to_string());
}
- assert_eq!(get_count(&app).unwrap(), n);
+ assert_eq!(ctx.app.display_counter(), n.to_string());
- let res = app.handle_key_event(KeyCode::Left.into());
+ let res = ctx.handle_key_event(KeyCode::Left.into());
assert!(res.is_ok());
- assert_eq!(get_count(&app).unwrap(), n - 1);
+ assert_eq!(ctx.app.display_counter(), (n - 1).to_string());
}
#[test]
fn handle_key_event_exit() {
- let mut app = App::default();
- assert!(app.init_context().is_ok());
- let res = app.handle_key_event(KeyCode::Char('q').into());
+ let mut ctx = default_runtime();
+ let res = ctx.handle_key_event(KeyCode::Char('q').into());
assert!(res.is_ok());
- assert!(app.exit);
+ assert!(ctx.app.get_exit_status());
}
#[test]
fn handle_key_event_dec_error() {
- let mut app = App::default();
- assert!(app.init_context().is_ok());
- let res = app.handle_key_event(KeyCode::Left.into());
+ let mut ctx = default_runtime();
+ let res = ctx.handle_key_event(KeyCode::Left.into());
assert!(res.is_err());
let res_err = res.unwrap_err();
@@ -289,10 +211,12 @@ mod tests {
#[test]
fn handle_key_event_inc_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.handle_key_event(KeyCode::Right.into());
+ let mut ctx = default_runtime();
+ // no pub set_counter exposed, so we manually set this
+ for _ in 0..u8::MAX {
+ assert!(ctx.app.increment_counter().is_ok());
+ }
+ let res = ctx.handle_key_event(KeyCode::Right.into());
assert!(res.is_err());
let res_err = res.unwrap_err();