From 35e89a8d5f3a6bb2385cd96f433566f222d7d25a Mon Sep 17 00:00:00 2001 From: Kritika Sharma Date: Sat, 7 Oct 2023 15:24:37 +0530 Subject: [PATCH] support configurable color scheme (#18) --- Cargo.lock | 71 +++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 3 +- src/args.rs | 2 +- src/color_scheme.rs | 21 +++++++++++++ src/config.rs | 39 +++++++++++++++++++++--- src/expected_input.rs | 6 ++-- src/main.rs | 5 +-- src/runner.rs | 7 +++-- 8 files changed, 139 insertions(+), 15 deletions(-) create mode 100644 src/color_scheme.rs diff --git a/Cargo.lock b/Cargo.lock index e02118a..ace014e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -259,6 +259,7 @@ dependencies = [ "mockall", "predicates 3.0.3", "rand", + "ratatui", "serde", "serde_json", "tempfile", @@ -336,6 +337,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "indoc" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" + [[package]] name = "itertools" version = "0.10.5" @@ -345,6 +352,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" @@ -474,6 +490,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -488,7 +510,7 @@ checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" dependencies = [ "difflib", "float-cmp", - "itertools", + "itertools 0.10.5", "normalize-line-endings", "predicates-core", "regex", @@ -503,7 +525,7 @@ dependencies = [ "anstyle", "difflib", "float-cmp", - "itertools", + "itertools 0.10.5", "normalize-line-endings", "predicates-core", "regex", @@ -573,6 +595,23 @@ dependencies = [ "getrandom", ] +[[package]] +name = "ratatui" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e2e4cd95294a85c3b4446e63ef054eea43e0205b1fd60120c16b74ff7ff96ad" +dependencies = [ + "bitflags 2.3.3", + "cassowary", + "crossterm 0.27.0", + "indoc", + "itertools 0.11.0", + "paste", + "strum", + "unicode-segmentation", + "unicode-width", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -644,6 +683,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + [[package]] name = "ryu" version = "1.0.15" @@ -729,6 +774,28 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.33", +] + [[package]] name = "syn" version = "1.0.109" diff --git a/Cargo.toml b/Cargo.toml index badafea..b01c809 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ serde = { version = "1.0.188", features = ["derive"] } serde_json = "1.0.107" tempfile = "3.8.0" tui = "0.19.0" +ratatui = "0.23" [dev-dependencies] -assert_cmd = "2.0.12" \ No newline at end of file +assert_cmd = "2.0.12" diff --git a/src/args.rs b/src/args.rs index 2f13bc5..fe70ab7 100644 --- a/src/args.rs +++ b/src/args.rs @@ -30,5 +30,5 @@ pub struct Args { /// uppercase-ratio argument #[arg(long)] pub uppercase_ratio: Option, - + } diff --git a/src/color_scheme.rs b/src/color_scheme.rs new file mode 100644 index 0000000..b098227 --- /dev/null +++ b/src/color_scheme.rs @@ -0,0 +1,21 @@ +use ratatui::style::Color; + +#[derive(Debug)] +pub struct ColorScheme { + pub correct_match_fg: Color, + pub correct_match_bg: Color, + pub incorrect_match_fg: Color, + pub incorrect_match_bg: Color, +} + +impl ColorScheme { + + pub fn default() -> Self { + Self { + correct_match_fg: Color::Green, + correct_match_bg: Color::Black, + incorrect_match_fg: Color::Gray, + incorrect_match_bg: Color::Red, + } + } +} diff --git a/src/config.rs b/src/config.rs index 96b36a6..57a5013 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,7 +8,7 @@ //! | `numbers` | `false` | boolean | flag indicating if numbers should be inserted in expected input | //! | `numbers_ratio` | `0.05` if numbers=TRUE | number | ratio for putting numbers in the test | //! | `dictionary_path` | `"src/dict/words.txt"` | string | dictionary words to sample from while creating test's expected input | -//! +//! //! `NOTE: If provided numbers_ratio is not between 0 to 1.0, Default numbers_ratio = 0.05 will be used.` //! //! @@ -36,6 +36,8 @@ use mockall::*; use serde::{Deserialize, Serialize}; use std::{fs, io::Read, path::PathBuf, time::Duration}; +use crate::color_scheme::ColorScheme; + use crate::Args; /// Main program configuration @@ -47,6 +49,7 @@ pub struct Config { pub dictionary_path: PathBuf, pub uppercase: bool, pub uppercase_ratio: f64, + pub color_config: ColorScheme, } /// Used by `serde` crate to parse config file into a rust struct @@ -57,7 +60,16 @@ struct ConfigFile { pub numbers_ratio: Option, pub dictionary_path: Option, pub uppercase: Option, - pub uppercase_ratio: Option + pub uppercase_ratio: Option, + pub color_config: Option, +} + +#[derive(Deserialize, Serialize, Debug)] +struct ConfigFileColorScheme { + pub correct_match_fg: Option, + pub correct_match_bg: Option, + pub incorrect_match_fg: Option, + pub incorrect_match_bg: Option, } #[automock] @@ -70,7 +82,8 @@ impl Config { numbers_ratio: 0.05, dictionary_path: PathBuf::from("src/dict/words.txt"), uppercase: false, - uppercase_ratio: 0.45 + uppercase_ratio: 0.45, + color_config: ColorScheme::default(), } } @@ -136,6 +149,24 @@ fn augment_config_with_config_file(config: &mut Config, mut config_file: fs::Fil config.uppercase_ratio = uppercase_ratio } } + + if let Some(color_config) = config_from_file.color_config { + if let Some(correct_match_fg) = color_config.correct_match_fg { + config.color_config.correct_match_fg = correct_match_fg.parse().unwrap(); + } + + if let Some(correct_match_bg) = color_config.correct_match_bg { + config.color_config.correct_match_bg = correct_match_bg.parse().unwrap(); + } + + if let Some(incorrect_match_fg) = color_config.incorrect_match_fg { + config.color_config.incorrect_match_fg = incorrect_match_fg.parse().unwrap(); + } + + if let Some(incorrect_match_bg) = color_config.incorrect_match_bg { + config.color_config.incorrect_match_bg = incorrect_match_bg.parse().unwrap(); + } + } } Ok(()) @@ -159,7 +190,7 @@ fn augment_config_with_args(config: &mut Config, args: Args) { if numbers_ratio >= 0.0 && numbers_ratio <= 1.0 { config.numbers_ratio = numbers_ratio } - } + } if let Some(duration) = args.duration { config.duration = Duration::from_secs(duration); } diff --git a/src/expected_input.rs b/src/expected_input.rs index 4fa3140..f288af4 100644 --- a/src/expected_input.rs +++ b/src/expected_input.rs @@ -98,7 +98,7 @@ fn create_uppercase_words (string_vec: &mut Vec, pos: usize, uppercase_r v[0] = v[0].to_uppercase().nth(0).unwrap(); let s: String = v.into_iter().collect(); string_vec[i] = s; - } + } } } @@ -125,6 +125,7 @@ impl ExpectedInputInterface for ExpectedInput { #[cfg(test)] mod tests { use std::{io::Write, time::Duration}; + use crate::color_scheme::ColorScheme; use super::*; @@ -148,7 +149,8 @@ mod tests { numbers_ratio: 0.05, dictionary_path: config_file.path().to_path_buf(), uppercase: false, - uppercase_ratio: 0.45 + uppercase_ratio: 0.45, + color_config: ColorScheme::default(), }; let expected_input = ExpectedInput::new(&config).expect("unable to create expected input"); diff --git a/src/main.rs b/src/main.rs index b0876f4..f8ca507 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,6 +50,7 @@ mod args; mod config; +mod color_scheme; mod expected_input; mod runner; @@ -63,7 +64,7 @@ use crossterm::{ }, }; use std::io; -use tui::{backend::CrosstermBackend, Terminal}; +use ratatui::{backend::CrosstermBackend, Terminal}; use args::Args; use config::Config; @@ -170,7 +171,7 @@ mod tests { use anyhow::{Context, Result}; use predicates::Predicate; - use tui::{backend::TestBackend, buffer::Buffer, Frame, Terminal}; + use ratatui::{backend::TestBackend, buffer::Buffer, Frame, Terminal}; use crate::{ args::Args, diff --git a/src/runner.rs b/src/runner.rs index 109b250..d40f9ac 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -15,7 +15,8 @@ use anyhow::{Context, Result}; use crossterm::event::{self, Event, KeyCode, KeyModifiers}; use mockall::automock; use std::time::{Duration, Instant}; -use tui::{ + +use ratatui::{ backend::Backend, layout::{Alignment, Constraint, Direction, Layout, Rect}, style::{Color, Style}, @@ -307,8 +308,8 @@ impl Runner { { let input = Paragraph::new(expected_input_char.to_string()).style( match input_char == expected_input_char { - true => Style::default().fg(Color::Green), - false => Style::default().bg(Color::Red).fg(Color::Gray), + true => Style::default().bg(self.config.color_config.correct_match_bg).fg(self.config.color_config.correct_match_fg), + false => Style::default().bg(self.config.color_config.incorrect_match_bg).fg(self.config.color_config.incorrect_match_fg), }, ); frame.render_widget(