From ba0028b7a77e39792e7ad7f35c24e061e78c14ee Mon Sep 17 00:00:00 2001 From: GarandPLG Date: Fri, 27 Mar 2026 20:15:15 +0100 Subject: [PATCH] Refactor keybindings and add validated CLI arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extracted common quit/esc handling into `common_keybindings`. Introduced `Group::CtrlMovement` for Ctrl‑arrow keys and updated keybinding definitions. Removed duplicated quit logic in main_menu and skirmish modules. Bumped clap to 4.6.0 and updated Cargo.lock. Added range‑checked parsers for map size, resources, supply limit, XP modifier and skill points in the CLI. --- Cargo.lock | 24 ++++++++-------- Cargo.toml | 1 - src/app/app.rs | 12 ++++---- src/app/keybindings/default.rs | 19 +++++++------ src/app/keybindings/keybindings.rs | 11 ++++---- src/app/keybindings/main_menu.rs | 5 ++-- src/app/keybindings/mod.rs | 2 +- src/app/keybindings/skirmish.rs | 8 ++---- src/cli.rs | 44 ++++++++++++++++++++++-------- 9 files changed, 73 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa0ef78..668ecd4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "anstream" -version = "0.6.21" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", "anstyle-parse", @@ -40,9 +40,9 @@ checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ "utf8parse", ] @@ -165,9 +165,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "clap" -version = "4.5.60" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" +checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" dependencies = [ "clap_builder", "clap_derive", @@ -175,9 +175,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.60" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ "anstream", "anstyle", @@ -187,9 +187,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.55" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" dependencies = [ "heck", "proc-macro2", @@ -963,9 +963,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.44" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] diff --git a/Cargo.toml b/Cargo.toml index 9ada30f..c8d216a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,5 @@ strip = true [dependencies] clap = { version = "4.5.53", features = ["derive"] } ratatui = "0.30.0" - log = "0.4.29" simplelog = "0.12.2" diff --git a/src/app/app.rs b/src/app/app.rs index 8b714b5..55ea8d8 100644 --- a/src/app/app.rs +++ b/src/app/app.rs @@ -69,21 +69,19 @@ pub fn handle_input_events(tx: mpsc::Sender) { loop { match event::read() { Ok(ev) => match ev { - event::Event::Key(key_event) => { - if tx.send(Event::Input(key_event)).is_err() { + event::Event::Key(key) => { + if tx.send(Event::Input(key)).is_err() { break; } } - event::Event::Resize(cols, rows) => { - if tx.send(Event::Resize(cols, rows)).is_err() { + event::Event::Resize(c, r) => { + if tx.send(Event::Resize(c, r)).is_err() { break; } } _ => {} }, - Err(_) => { - continue; - } + Err(_) => continue, } } } diff --git a/src/app/keybindings/default.rs b/src/app/keybindings/default.rs index fd04762..6e2655e 100644 --- a/src/app/keybindings/default.rs +++ b/src/app/keybindings/default.rs @@ -4,13 +4,16 @@ use crate::app::{ }; use ratatui::crossterm::event::KeyEvent; -pub fn default_keybindings(app: &mut App, key_event: &KeyEvent) { - if let Some(action) = event_to_action(&key_event) { - match action { - Action::Quit => app.exit = true, - Action::Quit2 => app.exit = true, - Action::Esc => app.view = View::MainMenu, - _ => (), - } +pub fn common_keybindings(app: &mut App, action: Action) { + match action { + Action::Quit | Action::Quit2 => app.exit = true, + Action::Esc => app.view = View::MainMenu, + _ => (), + } +} + +pub fn default_keybindings(app: &mut App, key_event: &KeyEvent) { + if let Some(action) = event_to_action(&key_event) { + common_keybindings(app, action); } } diff --git a/src/app/keybindings/keybindings.rs b/src/app/keybindings/keybindings.rs index fbf334a..4972b0c 100644 --- a/src/app/keybindings/keybindings.rs +++ b/src/app/keybindings/keybindings.rs @@ -22,6 +22,7 @@ pub enum Action { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ValueEnum)] pub enum Group { + CtrlMovement, Movement, Scroll, Select, @@ -100,7 +101,7 @@ pub static KEYBINDINGS: &[KeyBinding] = &[ code: KeyCode::Up, kind: KeyEventKind::Press, modifiers: KeyModifiers::CONTROL, - group: Group::Movement, + group: Group::CtrlMovement, symbol: "Ctrl + ↑", description: "Scroll Up ", }, @@ -109,7 +110,7 @@ pub static KEYBINDINGS: &[KeyBinding] = &[ code: KeyCode::Down, kind: KeyEventKind::Press, modifiers: KeyModifiers::CONTROL, - group: Group::Movement, + group: Group::CtrlMovement, symbol: "Ctrl + ↓", description: "Scroll Down", }, @@ -118,7 +119,7 @@ pub static KEYBINDINGS: &[KeyBinding] = &[ code: KeyCode::Left, kind: KeyEventKind::Press, modifiers: KeyModifiers::CONTROL, - group: Group::Movement, + group: Group::CtrlMovement, symbol: "Ctrl + ←", description: "Scroll Left", }, @@ -127,7 +128,7 @@ pub static KEYBINDINGS: &[KeyBinding] = &[ code: KeyCode::Right, kind: KeyEventKind::Press, modifiers: KeyModifiers::CONTROL, - group: Group::Movement, + group: Group::CtrlMovement, symbol: "Ctrl + →", description: "Scroll Right", }, @@ -156,7 +157,7 @@ pub static KEYBINDINGS: &[KeyBinding] = &[ modifiers: KeyModifiers::NONE, group: Group::Movement, symbol: "Esc", - description: "Go back", + description: "Go back to main menu", }, KeyBinding { action: Action::Backspace, diff --git a/src/app/keybindings/main_menu.rs b/src/app/keybindings/main_menu.rs index e29cd87..ce55e24 100644 --- a/src/app/keybindings/main_menu.rs +++ b/src/app/keybindings/main_menu.rs @@ -1,14 +1,13 @@ use crate::app::{ App, View, - keybindings::{Action, event_to_action}, + keybindings::{Action, common_keybindings, event_to_action}, }; use ratatui::crossterm::event::KeyEvent; pub fn main_menu_keybindings(app: &mut App, event: &KeyEvent) { if let Some(action) = event_to_action(&event) { + common_keybindings(app, action); match action { - Action::Quit => app.exit = true, - Action::Quit2 => app.exit = true, Action::Up => { app.states.main_menu.selected_view = app.states.main_menu.selected_view.saturating_sub(1).max(1); diff --git a/src/app/keybindings/mod.rs b/src/app/keybindings/mod.rs index 5f0dc81..2696876 100644 --- a/src/app/keybindings/mod.rs +++ b/src/app/keybindings/mod.rs @@ -3,7 +3,7 @@ pub mod keybindings; pub mod main_menu; pub mod skirmish; -pub use default::default_keybindings; +pub use default::{common_keybindings, default_keybindings}; pub use keybindings::{Action, Group, KEYBINDINGS, KeyBinding, binding_for, event_to_action}; pub use main_menu::main_menu_keybindings; pub use skirmish::skirmish_keybindings; diff --git a/src/app/keybindings/skirmish.rs b/src/app/keybindings/skirmish.rs index 6c9d82b..afa8755 100644 --- a/src/app/keybindings/skirmish.rs +++ b/src/app/keybindings/skirmish.rs @@ -1,19 +1,17 @@ use crate::app::{ - App, View, - keybindings::{Action, event_to_action}, + App, + keybindings::{Action, common_keybindings, event_to_action}, }; use ratatui::crossterm::event::KeyEvent; pub fn skirmish_keybindings(app: &mut App, key_event: &KeyEvent) { if let Some(action) = event_to_action(&key_event) { + common_keybindings(app, action); match action { Action::ScrollUp => app.states.skirmish.vertical_offset.prev(), Action::ScrollDown => app.states.skirmish.vertical_offset.next(), Action::ScrollLeft => app.states.skirmish.horizontal_offset.prev(), Action::ScrollRight => app.states.skirmish.horizontal_offset.next(), - Action::Quit => app.exit = true, - Action::Quit2 => app.exit = true, - Action::Esc => app.view = View::MainMenu, _ => (), } } diff --git a/src/cli.rs b/src/cli.rs index 91b50ee..5b46828 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -2,7 +2,8 @@ use crate::app::{ states::{GameMode, PerkDecks}, view::View, }; -use clap::Parser; +use clap::{Error, Parser, error::ErrorKind, value_parser}; +use std::num::ParseFloatError; #[derive(Parser, Debug)] #[command(version, about = "War in Tunnels", long_about = "War in Tunnels")] @@ -37,7 +38,8 @@ pub struct Cli { long, help = "Map width", value_name = "Positive integer [20; 100]", - default_value = "50" + default_value = "50", + value_parser = value_parser!(u8).range(20..=100) )] pub map_width: u8, @@ -45,7 +47,8 @@ pub struct Cli { long, help = "Map height", value_name = "Positive integer [11; 50]", - default_value = "25" + default_value = "25", + value_parser = value_parser!(u8).range(25..=50) )] pub map_height: u8, @@ -61,24 +64,27 @@ pub struct Cli { #[arg( long, help = "Starting wood", - value_name = "Positive integer", - default_value = "50" + value_name = "Positive integer [1; 150]", + default_value = "50", + value_parser = value_parser!(u16).range(1..=150) )] pub starting_wood: u16, #[arg( long, help = "Starting iron", - value_name = "Positive integer", - default_value = "25" + value_name = "Positive integer [1; 150]", + default_value = "25", + value_parser = value_parser!(u16).range(1..=150) )] pub starting_iron: u16, #[arg( long, help = "Supply limit", - value_name = "Positive integer", - default_value = "99" + value_name = "Positive integer [49; 149]", + default_value = "99", + value_parser = value_parser!(u8).range(49..=149) )] pub supply_limit: u8, @@ -86,7 +92,8 @@ pub struct Cli { long, help = "XP modifier", value_name = "Float [0.5; 2.0]", - default_value = "1.0" + default_value = "1.0", + value_parser = parse_xp )] pub xp_modifier: f32, @@ -94,7 +101,8 @@ pub struct Cli { long, help = "Skill points limit", value_name = "Positive integer [120; 690]", - default_value = "120" + default_value = "120", + value_parser = value_parser!(u16).range(120..=690) )] pub skill_points_limit: u16, @@ -109,3 +117,17 @@ pub struct Cli { pub fn get_args() -> Cli { Cli::parse() } + +fn parse_xp(s: &str) -> Result { + let v: f32 = s + .parse() + .map_err(|e: ParseFloatError| Error::raw(ErrorKind::InvalidValue, e.to_string()))?; + if (0.5..=2.0).contains(&v) { + Ok(v) + } else { + Err(Error::raw( + ErrorKind::InvalidValue, + format!("Value {} is out of range 0.5..=2.0", v), + )) + } +}