From 356b4b172806b7e63f35920950b18b535651de52 Mon Sep 17 00:00:00 2001 From: 0Ky <16103757+0Ky@users.noreply.github.com> Date: Sat, 21 Oct 2023 08:44:49 +1100 Subject: [PATCH 1/4] fix(paths): add os specific directory handling for windows Adjusted the path generation in `get_results_dir_path`, `handle_main_command`, and `setup_terminal` to account for Windows-specific local config directory conventions using the `cfg!` macro. --- src/main.rs | 29 +++++++++++++++++++---------- src/test_results.rs | 14 +++++++++----- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/main.rs b/src/main.rs index a34197a..a94dcd6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -130,11 +130,16 @@ fn handle_main_command( terminal: &mut Terminal>, args: Args, ) -> Result<()> { - let config_file_path = dirs::home_dir() - .context("Unable to get home directory")? - .join(".config") - .join("donkeytype") - .join("donkeytype-config.json"); + let config_file_path = if cfg!(target_os = "windows") { + dirs::config_local_dir().context("Unable to get local config directory")? + } else { + dirs::home_dir() + .context("Unable to get home directory")? + .join(".config") + } + .join("donkeytype") + .join("donkeytype-config.json"); + let config = Config::new(args, config_file_path).context("Unable to create config")?; let expected_input = ExpectedInput::new(&config).context("Unable to create expected input")?; @@ -234,11 +239,15 @@ mod tests { } fn setup_terminal(args: Args) -> Result<(Config, ExpectedInput, Terminal)> { - let config_file_path = dirs::home_dir() - .context("Unable to get home directory")? - .join(".config") - .join("donkeytype") - .join("donkeytype-config.json"); + let config_file_path = if cfg!(target_os = "windows") { + dirs::config_local_dir().context("Unable to get local config directory")? + } else { + dirs::home_dir() + .context("Unable to get home directory")? + .join(".config") + } + .join("donkeytype") + .join("donkeytype-config.json"); let config = Config::new(args, config_file_path).context("Unable to create config")?; let expected_input = diff --git a/src/test_results.rs b/src/test_results.rs index e9164eb..0b34479 100644 --- a/src/test_results.rs +++ b/src/test_results.rs @@ -404,11 +404,15 @@ fn render_chart( } fn get_results_dir_path() -> Result { - let dir_path = dirs::home_dir() - .context("Unable to get home directory")? - .join(".local") - .join("share") - .join("donkeytype"); + let dir_path = if cfg!(target_os = "windows") { + dirs::config_local_dir().context("Unable to get local config directory")? + } else { + dirs::home_dir() + .context("Unable to get home directory")? + .join(".local") + .join("share") + } + .join("donkeytype"); Ok(dir_path) } From 369c08a63d52d575dc373730ae1ecefa2c6d379a Mon Sep 17 00:00:00 2001 From: 0Ky <16103757+0Ky@users.noreply.github.com> Date: Tue, 24 Oct 2023 09:47:49 +1100 Subject: [PATCH 2/4] fix: windows double keypress issue --- src/runner.rs | 126 ++++++++++++++++++++++---------------------- src/test_results.rs | 22 ++++---- 2 files changed, 77 insertions(+), 71 deletions(-) diff --git a/src/runner.rs b/src/runner.rs index c04b3a1..7cd1c4d 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -12,7 +12,7 @@ //! And test statistics are returned from the runner. use anyhow::{Context, Result}; -use crossterm::event::{self, Event, KeyCode, KeyModifiers}; +use crossterm::event::{self, Event, KeyCode, KeyModifiers, KeyEventKind}; use mockall::automock; use std::time::{Duration, Instant}; @@ -106,68 +106,70 @@ impl Runner { if event::poll(timeout).context("Unable to poll for event")? { if let Event::Key(key) = event::read().context("Unable to read event")? { - match self.input_mode { - InputMode::Normal => match key.code { - KeyCode::Char('e') => { - start_time = if is_started { - start_time + pause_time.elapsed() - } else { - Instant::now() - }; - is_started = true; - self.input_mode = InputMode::Editing; - } - KeyCode::Char('q') => { - // todo return canceled test error and handle it in main - return Ok(TestResults::new( - Stats::default(), - self.config.clone(), - false, - )); - } - _ => {} - }, - InputMode::Editing => match key.code { - // Crossterm returns `ctrl+w` or ``ctrl+h` when `ctrl+backspace` is pressed - // see: https://github.com/crossterm-rs/crossterm/issues/504 - KeyCode::Char('h') | KeyCode::Char('w') - if key.modifiers.contains(KeyModifiers::CONTROL) => - { - self.remove_last_word(); - } - KeyCode::Char(c) => { - self.input.push(c); - - let expected_input = self - .expected_input - .get_string(self.input.len()) - .chars() - .collect::>(); - - let is_correct = - self.input.chars().last() == expected_input.last().copied(); - - if !is_correct { - self.raw_mistakes_count += 1; - } else { - self.raw_valid_characters_count += 1; + if key.kind == KeyEventKind::Press { + match self.input_mode { + InputMode::Normal => match key.code { + KeyCode::Char('e') => { + start_time = if is_started { + start_time + pause_time.elapsed() + } else { + Instant::now() + }; + is_started = true; + self.input_mode = InputMode::Editing; } - } - KeyCode::Backspace - if key.modifiers.contains(KeyModifiers::ALT) - | key.modifiers.contains(KeyModifiers::CONTROL) => - { - self.remove_last_word(); - } - KeyCode::Backspace => { - self.input.pop(); - } - KeyCode::Esc => { - pause_time = Instant::now(); - self.input_mode = InputMode::Normal; - } - _ => {} - }, + KeyCode::Char('q') => { + // todo return canceled test error and handle it in main + return Ok(TestResults::new( + Stats::default(), + self.config.clone(), + false, + )); + } + _ => {} + }, + InputMode::Editing => match key.code { + // Crossterm returns `ctrl+w` or ``ctrl+h` when `ctrl+backspace` is pressed + // see: https://github.com/crossterm-rs/crossterm/issues/504 + KeyCode::Char('h') | KeyCode::Char('w') + if key.modifiers.contains(KeyModifiers::CONTROL) => + { + self.remove_last_word(); + } + KeyCode::Char(c) => { + self.input.push(c); + + let expected_input = self + .expected_input + .get_string(self.input.len()) + .chars() + .collect::>(); + + let is_correct = + self.input.chars().last() == expected_input.last().copied(); + + if !is_correct { + self.raw_mistakes_count += 1; + } else { + self.raw_valid_characters_count += 1; + } + } + KeyCode::Backspace + if key.modifiers.contains(KeyModifiers::ALT) + | key.modifiers.contains(KeyModifiers::CONTROL) => + { + self.remove_last_word(); + } + KeyCode::Backspace => { + self.input.pop(); + } + KeyCode::Esc => { + pause_time = Instant::now(); + self.input_mode = InputMode::Normal; + } + _ => {} + }, + } } } } diff --git a/src/test_results.rs b/src/test_results.rs index 0b34479..a499637 100644 --- a/src/test_results.rs +++ b/src/test_results.rs @@ -5,7 +5,7 @@ use anyhow::{Context, Result}; use chrono::{DateTime, Datelike, Local, Timelike}; -use crossterm::event::{self, Event, KeyCode}; +use crossterm::event::{self, Event, KeyCode, KeyEventKind}; use ratatui::{ prelude::{Backend, Constraint, Direction, Layout, Rect}, style::{Style, Stylize}, @@ -196,11 +196,13 @@ impl TestResults { if event::poll(Duration::from_millis(100)).context("Unable to poll for event")? { if let Event::Key(key) = event::read().context("Unable to read event")? { - match key.code { - KeyCode::Esc => { - break; + if key.kind == KeyEventKind::Press { + match key.code { + KeyCode::Esc => { + break; + } + _ => {} } - _ => {} } } } @@ -313,11 +315,13 @@ pub fn render_results( if event::poll(Duration::from_millis(100)).context("Unable to poll for event")? { if let Event::Key(key) = event::read().context("Unable to read event")? { - match key.code { - KeyCode::Esc => { - break; + if key.kind == KeyEventKind::Press { + match key.code { + KeyCode::Esc => { + break; + } + _ => {} } - _ => {} } } } From 078c65c85921dcaa5bbbf4e0098abe88a2c27b93 Mon Sep 17 00:00:00 2001 From: 0Ky <16103757+0Ky@users.noreply.github.com> Date: Tue, 24 Oct 2023 09:55:01 +1100 Subject: [PATCH 3/4] style(fmt): apply cargo fmt --- src/runner.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runner.rs b/src/runner.rs index 7cd1c4d..f0c8870 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -12,7 +12,7 @@ //! And test statistics are returned from the runner. use anyhow::{Context, Result}; -use crossterm::event::{self, Event, KeyCode, KeyModifiers, KeyEventKind}; +use crossterm::event::{self, Event, KeyCode, KeyEventKind, KeyModifiers}; use mockall::automock; use std::time::{Duration, Instant}; From 3af2f79c0b95fd5c77d1fe0cacd0e39942949d57 Mon Sep 17 00:00:00 2001 From: 0Ky <16103757+0Ky@users.noreply.github.com> Date: Tue, 24 Oct 2023 10:10:09 +1100 Subject: [PATCH 4/4] feat(workflows): enable windows-gnu target release --- .github/workflows/release.yml | 60 +++++++++++++++++------------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e16c49a..8edaea0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,30 +1,30 @@ -on: - release: - types: [created] - -jobs: - release: - name: release ${{ matrix.target }} - runs-on: ubuntu-latest - permissions: - contents: write - strategy: - fail-fast: false - matrix: - include: - # - target: x86_64-pc-windows-gnu - # archive: zip - # - target: x86_64-unknown-linux-musl - # archive: tar.gz tar.xz tar.zst - - target: x86_64-apple-darwin - archive: zip - steps: - - uses: actions/checkout@master - - name: Compile and release - uses: rust-build/rust-build.action@v1.4.3 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TOOLCHAIN_VERSION: 1.70.0 - with: - RUSTTARGET: ${{ matrix.target }} - ARCHIVE_TYPES: ${{ matrix.archive }} +on: + release: + types: [created] + +jobs: + release: + name: release ${{ matrix.target }} + runs-on: ubuntu-latest + permissions: + contents: write + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-pc-windows-gnu + archive: zip + # - target: x86_64-unknown-linux-musl + # archive: tar.gz tar.xz tar.zst + - target: x86_64-apple-darwin + archive: zip + steps: + - uses: actions/checkout@master + - name: Compile and release + uses: rust-build/rust-build.action@v1.4.3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TOOLCHAIN_VERSION: 1.70.0 + with: + RUSTTARGET: ${{ matrix.target }} + ARCHIVE_TYPES: ${{ matrix.archive }}