From 6f17406d983c0b94fec6b3d32a0699a7820de2e4 Mon Sep 17 00:00:00 2001 From: GarandPLG Date: Sun, 29 Mar 2026 15:36:11 +0200 Subject: [PATCH] Add documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extensive doc comments were added to keybindings, CLI options, logging, and widget modules. The `count_largest_group` function was introduced to compute the largest group size among actions. CLI validation for `skill_points_limit` was broadened to a 90‑690 range. Imports and signatures for `Display` were adjusted accordingly. --- src/app/keybindings/keybindings.rs | 59 ++++++++++++++++++++++++++++++ src/app/states/perk_decks.rs | 4 +- src/app/widgets/keybindings.rs | 28 ++++++++++++++ src/cli.rs | 38 ++++++++++++++++++- src/logs.rs | 26 +++++++++++++ src/main.rs | 5 +++ 6 files changed, 156 insertions(+), 4 deletions(-) diff --git a/src/app/keybindings/keybindings.rs b/src/app/keybindings/keybindings.rs index d2ecc53..efe8024 100644 --- a/src/app/keybindings/keybindings.rs +++ b/src/app/keybindings/keybindings.rs @@ -2,49 +2,95 @@ use clap::ValueEnum; use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers}; use std::collections::HashMap; +/// Represents the set of actions that the UI can perform. +/// +/// Each variant corresponds to a concrete operation that can be triggered by a +/// key binding. The `WildCard` variant is a special case that matches any +/// character key and carries the actual character pressed. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Action { + /// Request the application to quit. Quit, + /// Alternate quit action. Quit2, + /// Move the selection cursor up. Up, + /// Move the selection cursor down. Down, + /// Move the selection cursor left. Left, + /// Move the selection cursor right. Right, + /// Scroll up without moving the cursor. ScrollUp, + /// Scroll down without moving the cursor. ScrollDown, + /// Scroll left without moving the cursor. ScrollLeft, + /// Scroll right without moving the cursor. ScrollRight, + /// Select the current item. Space, + /// Submit or confirm the current choice. Enter, + /// Return to the main menu or previous screen. Esc, + /// Delete the character before the cursor. Backspace, + /// Zoom the view in. ZoomIn, + /// Zoom the view out. ZoomOut, + /// Matches any character key; the inner `char` is the actual key pressed. WildCard(char), } +/// Logical groups of key bindings used for organising help screens or +/// configuration sections. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ValueEnum)] pub enum Group { + /// Bindings that combine `Ctrl` with movement keys. CtrlMovement, + /// Plain movement bindings. Movement, + /// Scroll bindings. Scroll, + /// Selection bindings. Select, + /// Text input related bindings. Input, + /// Zoom related bindings. Zoom, + /// Quit related bindings. Quit, } +/// Description of a single key binding. +/// +/// This struct stores the mapping from a physical key event to an `Action`, +/// together with metadata used for displaying help information. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct KeyBinding { + /// The logical action performed when this binding is triggered. pub action: Action, + /// The raw `crossterm::event::KeyCode` that identifies the key. pub code: KeyCode, + /// The kind of key event (crossterm::event::KeyEventKind). pub kind: KeyEventKind, + /// Any modifier keys that must be held (crossterm::event::KeyModifiers). pub modifiers: KeyModifiers, + /// The group this binding belongs to, useful for categorising help. pub group: Group, + /// Human‑readable symbol shown in UI/help screens. pub symbol: &'static str, + /// Short description of what the binding does. pub description: &'static str, } +/// Complete list of all key bindings used by the application. +/// +/// The slice is ordered primarily for readability; the runtime lookup scans +/// this slice linearly, which is acceptable given the modest size of the list. pub static KEYBINDINGS: &[KeyBinding] = &[ KeyBinding { action: Action::Quit, @@ -201,10 +247,18 @@ pub static KEYBINDINGS: &[KeyBinding] = &[ }, ]; +/// Returns the `KeyBinding` associated with the given `Action`, if one exists. pub fn binding_for(action: Action) -> Option<&'static KeyBinding> { KEYBINDINGS.iter().find(|b| b.action == action) } +/// Converts a raw `KeyEvent` into an `Action` if a matching binding is found. +/// +/// The function first looks for an exact match on code, kind, and modifiers. +/// If no exact match exists but a wildcard binding is present for the same +/// event kind, the function returns `Action::WildCard` with the character that +/// was pressed. Returns `None` when the event does not correspond to any known +/// action. pub fn event_to_action(event: &KeyEvent) -> Option { if let Some(b) = KEYBINDINGS .iter() @@ -225,6 +279,11 @@ pub fn event_to_action(event: &KeyEvent) -> Option { None } +/// Determines the size of the largest group among a slice of actions. +/// +/// It counts how many times each group appears in the supplied actions and +/// returns the highest count. This can be used, for example, to size UI +/// elements that display groups of bindings. pub fn count_largest_group(actions: &Vec) -> u16 { let mut group_counts: HashMap = HashMap::new(); diff --git a/src/app/states/perk_decks.rs b/src/app/states/perk_decks.rs index a9d4bb8..ff3fd18 100644 --- a/src/app/states/perk_decks.rs +++ b/src/app/states/perk_decks.rs @@ -1,5 +1,5 @@ use clap::ValueEnum; -use std::fmt::Display; +use std::fmt::{Display, Formatter, Result}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct PerkDecksState { @@ -16,7 +16,7 @@ pub enum PerkDecks { } impl Display for PerkDecks { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { match self { PerkDecks::Silesian => write!(f, "Silesian"), PerkDecks::BogeyMan => write!(f, "Bogey Man"), diff --git a/src/app/widgets/keybindings.rs b/src/app/widgets/keybindings.rs index ddc9814..e655fe1 100644 --- a/src/app/widgets/keybindings.rs +++ b/src/app/widgets/keybindings.rs @@ -12,11 +12,31 @@ use ratatui::{ }; use std::collections::HashSet; +/// A widget that visualises the keybindings for a set of actions. +/// +/// The widget stores a vector of [`Paragraph`] objects, each representing one +/// logical group of keybindings (e.g. navigation, editing, etc.). The paragraphs +/// are rendered side‑by‑side inside a bordered container. pub struct KeybindingsWidget { + /// Paragraphs, one per used `Group`, containing the formatted keybinding lines. grouped: Vec>, } impl KeybindingsWidget { + /// Create a new widget from a list of actions. + /// + /// For each action, the corresponding static [`KeyBinding`] is looked up + /// using [`binding_for`]. Only groups that have at least one binding are + /// displayed. The resulting widget contains a paragraph for each group, + /// formatted with the key symbol (red & bold) followed by its description. + /// + /// # Parameters + /// + /// * `actions` – A vector of actions for which keybindings should be shown. + /// + /// # Returns + /// + /// A fully‑initialised `KeybindingsWidget` ready for rendering. pub fn new(actions: Vec) -> Self { let keybindings: Vec> = actions.iter().map(|a| binding_for(*a)).collect(); @@ -66,6 +86,14 @@ impl KeybindingsWidget { } impl Widget for KeybindingsWidget { + /// Render the widget inside the given rectangular area. + /// + /// The widget first draws a surrounding border with the title *Keybindings*. + /// It then divides the inner area into equal horizontal slices—one per + /// used group—and renders each group's paragraph into its slice. + /// + /// If no groups are present, the function returns early without drawing + /// anything. fn render(self, area: Rect, buf: &mut Buffer) { let count: u16 = self.grouped.len() as u16; diff --git a/src/cli.rs b/src/cli.rs index 2a6050d..cbc92de 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -5,9 +5,16 @@ use crate::app::{ use clap::{Error, Parser, error::ErrorKind, value_parser}; use std::num::ParseFloatError; +/// Holds all configurable options that can be passed via the command line. +/// +/// Each field corresponds to a CLI flag (e.g. `--view`, `--username`, …). +/// The `clap` attributes describe the flag name, help text, default value, +/// and any validation constraints. The struct derives `Parser` so that +/// `Cli::parse()` can be called directly to obtain a populated instance. #[derive(Parser, Debug)] #[command(version, about = "War in Tunnels", long_about = "War in Tunnels")] pub struct Cli { + /// The initial view/window to display. #[arg( long, help = "Default window", @@ -17,6 +24,7 @@ pub struct Cli { )] pub view: View, + /// Player name shown in the UI. #[arg( long, help = "Username", @@ -25,6 +33,7 @@ pub struct Cli { )] pub username: String, + /// Game mode selection. #[arg( long, help = "Game mode", @@ -34,6 +43,7 @@ pub struct Cli { )] pub game_mode: GameMode, + /// Width of the generated map (20–100 tiles). #[arg( long, help = "Map width", @@ -43,6 +53,7 @@ pub struct Cli { )] pub map_width: u8, + /// Height of the generated map (25–50 tiles). #[arg( long, help = "Map height", @@ -52,6 +63,7 @@ pub struct Cli { )] pub map_height: u8, + /// Zoom level used for the UI. #[arg( long, help = "Zoom level", @@ -61,6 +73,7 @@ pub struct Cli { )] pub zoom_level: ZoomLevel, + /// Which perk deck to use. #[arg( long, help = "Perk Deck", @@ -70,6 +83,7 @@ pub struct Cli { )] pub perk_deck: PerkDecks, + /// Starting amount of wood (1–150). #[arg( long, help = "Starting wood", @@ -79,6 +93,7 @@ pub struct Cli { )] pub starting_wood: u16, + /// Starting amount of iron (1–150). #[arg( long, help = "Starting iron", @@ -88,6 +103,7 @@ pub struct Cli { )] pub starting_iron: u16, + /// Maximum amount of units (49–149). #[arg( long, help = "Supply limit", @@ -97,6 +113,7 @@ pub struct Cli { )] pub supply_limit: u8, + /// Modifier applied to experience points (0.5–2.0). #[arg( long, help = "XP modifier", @@ -106,15 +123,17 @@ pub struct Cli { )] pub xp_modifier: f32, + /// Upper bound for skill points that can be earned (90–690). #[arg( long, help = "Skill points limit", - value_name = "Positive integer [120; 690]", + value_name = "Positive integer [90; 690]", default_value = "120", - value_parser = value_parser!(u16).range(120..=690) + value_parser = value_parser!(u16).range(90..=690) )] pub skill_points_limit: u16, + /// Enable logging to a file (default: disabled). #[arg( long, help = "Enable logging to file (default: disabled)", @@ -123,10 +142,25 @@ pub struct Cli { pub log: bool, } +/// Parses the command line arguments and returns a fully populated `Cli` +/// instance. This function simply forwards to `Cli::parse()`, which +/// handles argument validation and displays helpful error messages if +/// the user supplies invalid input. pub fn get_args() -> Cli { Cli::parse() } +/// Parses a string into a floating‑point XP modifier and validates that it +/// lies within the inclusive range `0.5..=2.0`. +/// +/// The function is used as a custom `value_parser` for the `--xp-modifier` +/// flag. On failure it returns a `clap::Error` with `ErrorKind::InvalidValue`, +/// allowing `clap` to present a clear error message to the user. +/// +/// # Errors +/// +/// * Returns an error if the string cannot be parsed as `f32`. +/// * Returns an error if the parsed value is outside the allowed range. fn parse_xp(s: &str) -> Result { let v: f32 = s .parse() diff --git a/src/logs.rs b/src/logs.rs index 50aee47..842582b 100644 --- a/src/logs.rs +++ b/src/logs.rs @@ -1,6 +1,32 @@ use simplelog::{Config, LevelFilter, WriteLogger}; use std::fs::File; +/// Initializes the global logger for the application. +/// +/// This function creates (or truncates) a file called `war_in_tunnels.log` +/// in the current working directory and configures a `simplelog::WriteLogger` +/// to write log messages with a minimum level of `Info`. +/// +/// # Panics +/// +/// - If the log file cannot be created (e.g., due to permission issues). +/// - If the logger cannot be initialized (which would indicate an internal +/// error in the `simplelog` crate). +/// +/// Call this once, preferably at the very beginning of `main()`, before any +/// logging macros are used. +/// +/// # Example +/// +/// ``` +/// use war_in_tunnels::logs::init_logger; +/// use log::info; +/// +/// fn main() { +/// init_logger(); +/// info!("Application started"); +/// } +/// ``` pub fn init_logger() { WriteLogger::init( LevelFilter::Info, diff --git a/src/main.rs b/src/main.rs index 4b9a91b..155ce32 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,11 @@ use war_in_tunnels::{ logs::init_logger, }; +/// Starts the terminal UI application. +/// +/// The function follows the steps outlined in the module‑level documentation. +/// It returns any `std::io::Error` encountered while initializing or restoring the +/// terminal, or while running the `App`. fn main() -> Result<()> { let args: Cli = get_args(); if args.log {