From 20e1995306895415bd80ef9f08e7bf1acc8d95e2 Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 23 Apr 2024 22:28:11 +0200 Subject: [PATCH] 22 allow skip input validation (#30) * removed re-export * removed print if invalid arguments * removed unsued code * reworked input Added: - Multiple Requirements - valid and Invalid Notes - additional notes for shotcuts - default option - support for ALLow_COLOR * update input example to work with the new input struct * fixed text not removing after condition match * update preview image * added documentation --- examples/input.rs | 37 +- images/rustrover64_Qgn5icero6.gif | Bin 22173 -> 26187 bytes src/color.rs | 5 +- src/menu/input.rs | 613 +++++++++++++++++++----------- src/terminal.rs | 13 +- 5 files changed, 431 insertions(+), 237 deletions(-) diff --git a/examples/input.rs b/examples/input.rs index 9427a48..7399428 100644 --- a/examples/input.rs +++ b/examples/input.rs @@ -1,20 +1,27 @@ use regex::Regex; -use zenity::menu::input::{valid_path, valid_regex}; + +use zenity::menu::input::{Input, Requirements}; fn main() { - println!("Input Preview:"); + println!("\n\nInput Preview:"); + + let path = Input::new("Enter Valid Path:", Requirements::default()).start(); + let path_with_regex = Input::new( + "Enter a Valid .toml file", + Requirements::path() + .set_regex(Regex::new(r"\.toml\z").unwrap()) + .set_note("", "Please Enter a Valid Path to a .toml file"), + ) + .start(); + let regex = Input::new( + "Enter Valid Regex (ABC):", + Requirements::regex(Regex::new(r"^ABC$").unwrap()), + ) + .allow_force() + .set_default("ABC") + .start(); - println!( - "\n\nReturn: {}", - valid_regex( - "Enter string:", - Regex::new(r"^\d{3}$").unwrap(), - Some("369"), - false - ) - ); - println!( - "\n\nPath: {:?}", - valid_path("Enter A Valid Path:", None, true) - ); + println!("Existing Path: {:?}", path); + println!("Path with Regex: {:?}", path_with_regex); + println!("Regex: {:?}", regex); } diff --git a/images/rustrover64_Qgn5icero6.gif b/images/rustrover64_Qgn5icero6.gif index ef4ca799b853006030b10e26673eebaa233c2b77..3f17984f66d749dfd48ef1ebe190837b023bd566 100644 GIT binary patch literal 26187 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 literal 22173 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 diff --git a/src/color.rs b/src/color.rs index fa33c98..e5727c9 100644 --- a/src/color.rs +++ b/src/color.rs @@ -143,10 +143,7 @@ impl CliColorConfig { "--color=always" => ColorOption::Always, "--color=never" => ColorOption::Never, _ => { - eprintln!("Invalid argument: {}", arg); - eprintln!("Usage: my_program [--color=always|auto|never]"); - eprintln!("Using Default: auto"); - + // removed error message print ColorOption::Auto } }; diff --git a/src/menu/input.rs b/src/menu/input.rs index 084ca81..f2668c4 100644 --- a/src/menu/input.rs +++ b/src/menu/input.rs @@ -11,38 +11,9 @@ //! //! # Examples //! -//! ## Validate Input Against Regex -//! -//! ```rust,ignore -//! use regex::Regex; -//! use zenity::menu::input::valid_regex; -//! -//! // Define a regex pattern to match three digits -//! let regex = Regex::new(r"^\d{3}$").unwrap(); -//! -//! // Prompt the user to enter input matching the regex pattern -//! let input = valid_regex(regex); -//! -//! println!("Valid input: {}", input); -//! ``` -//! -//! ## Validate File Path -//! -//! ```rust,ignore -//! use std::path::PathBuf; -//! use zenity::menu::input::valid_path; -//! -//! // Prompt the user to enter a valid file path -//! let path: PathBuf = (*valid_path()).clone().into(); // Cloning the Box and then converting it into PathBuf -//! -//! println!("Entered path: {:?}", path); -//! ``` -//! -//! This module is a work in progress, and contributions are welcome -//! use std::io; -use std::path::{Path, PathBuf}; +use std::path::Path; use crossterm::{ cursor, execute, @@ -50,238 +21,446 @@ use crossterm::{ }; use regex::Regex; +use crate::color::ENABLE_COLOR; use crate::menu::handle_key_input; use crate::style::{Color, Print, SetForegroundColor}; +use crate::terminal::console_render::raw_mode_wrapper; + +/// Represents requirements for validating user input +/// +/// # Examples +/// +/// ``` +/// use zenity::menu::input::Requirements; +/// +/// // Create default requirements for validating paths +/// let default_requirements = Requirements::default(); +/// ``` +pub struct Requirements { + /// Regex to match (optional if `path` is true) + regex: Option, + + /// If the input needs to be a valid path + /// + /// If valid, the `regex` will be applied to the name and extension + path: bool, + + /// Allow creating the path if it doesn't exist yet + /// + /// NOT WORKING yet will add asap + /// + /// **NOTES** + /// - The `regex` still needs to match + /// - This only works if `path` is true + allow_creating: bool, + + /// Note to display if the condition matches + true_note: Option, + + /// Note to display while the condition doesn't match + false_note: Option, +} + +impl Default for Requirements { + /// Creates default requirements for validating paths. + /// + /// # Examples + /// + /// ``` + /// use zenity::menu::input::Requirements; + /// + /// // Create default requirements for validating paths + /// let default_requirements = Requirements::default(); + /// ``` + fn default() -> Self { + Requirements::path() + } +} +impl Requirements { + /// Creates requirements with a specific regex + /// + /// # Examples + /// + /// ``` + /// use regex::Regex; + /// use zenity::menu::input::Requirements; + /// + /// // Create requirements with a specific regex + /// let regex = Regex::new(r"\d{4}-\d{2}-\d{2}").unwrap(); + /// let regex_requirements = Requirements::regex(regex); + /// ``` + pub fn regex(regex: Regex) -> Self { + Requirements { + regex: Some(regex), + path: false, + allow_creating: false, + true_note: None, + false_note: None, + } + } + + /// Creates requirements for validating paths + /// + /// # Examples + /// + /// ``` + /// use zenity::menu::input::Requirements; + /// + /// // Create requirements for validating paths + /// let path_requirements = Requirements::path(); + /// ``` + pub fn path() -> Self { + Requirements { + regex: None, + path: true, + allow_creating: false, + true_note: None, + false_note: Some("Please enter a valid path!".to_string()), + } + } + + /// Sets the regex for the requirements + /// + /// # Examples + /// + /// ``` + /// use regex::Regex; + /// use zenity::menu::input::Requirements; + /// + /// // Create requirements for validating paths + /// let mut path_requirements = Requirements::path(); + /// + /// // Set a custom regex for path validation + /// let regex = Regex::new(r"\d{4}-\d{2}-\d{2}").unwrap(); + /// path_requirements.set_regex(regex); + /// ``` + pub fn set_regex(mut self, regex: Regex) -> Self { + self.regex = Some(regex); + + self + } + + /// Sets notes to display based on validity + /// + /// # Examples + /// + /// ``` + /// use zenity::menu::input::Requirements; + /// + /// // Create requirements for validating paths + /// let mut path_requirements = Requirements::path(); + /// + /// // Set notes to display based on validity + /// path_requirements.set_note("Valid path.", "Invalid path."); + /// ``` + pub fn set_note(mut self, valid: &str, invalid: &str) -> Self { + self.true_note = Some(valid.to_string()); + self.false_note = Some(invalid.to_string()); + + self + } + + /// Allows creating the path if it doesn't exist yet + /// + /// # Examples + /// + /// ``` + /// use zenity::menu::input::Requirements; + /// + /// // Create requirements for validating paths + /// let mut path_requirements = Requirements::path(); + /// + /// // Allow creating the path if it doesn't exist yet + /// path_requirements.allow_creation(); + /// ``` + pub fn allow_creation(mut self) { + self.allow_creating = true; + } +} + +/// Represents an input field with validation requirements and optional default value. +pub struct Input { + /// The title or prompt displayed for the input field. + title: String, + /// The validation requirements for the input field. + requirements: Vec, + /// The default value that can be accepted by pressing Enter. + default: Option, + /// Indicates whether the input can be forced without meeting validation requirements. + allow_force: bool, +} + +impl Input { + /// Creates a new input field with the specified title and validation requirements. + /// + /// # Arguments + /// + /// * `title` - The title or prompt for the input field. + /// * `req` - The validation requirement for the input field more can be added with the ``add_requirement`` method + /// + /// # Returns + /// + /// A new `Input` instance with the given title and validation requirements. + pub fn new(title: &str, req: Requirements) -> Self { + let reqs = vec![req]; + + Input { + title: title.to_string(), + requirements: reqs, + default: None, + allow_force: false, + } + } -macro_rules! input_loop { - ($title:expr, $buffer:expr, $validate:expr, $default:expr, $allow_force:expr) => { + /// Starts the input process, displaying the prompt and handling user input. + /// + /// This method prompts the user for input, validates it according to the specified requirements, + /// and returns the validated input as a boxed string. + /// + /// # Returns + /// + /// A boxed string containing the validated input. + pub fn start(&self) -> Box { let mut force: bool = false; + let mut buffer = String::new(); + + // Initialize vectors to store validation status and notes + let mut validation_status = Vec::new(); + let mut notes = Vec::new(); loop { - render_input_prompt($title, &$buffer, &$validate, $default); + raw_mode_wrapper!(self.render_input_prompt( + &buffer, + validation_status.iter().all(|&status| status), + ¬es, + )); + + let result = handle_key_input(&mut buffer, &mut force); + // Perform validation for each requirement and store results + validation_status.clear(); + notes.clear(); + + for req in &self.requirements { + let mut path_valid = true; - if handle_key_input(&mut $buffer, &mut force) { - if !$buffer.is_empty() && $validate { + if req.path { + path_valid = Self::validate_path(&buffer); + } + + let regex_valid = req + .regex + .as_ref() + .map_or(true, |regex| Self::validate_regex(&buffer, regex)); + + // Push the validation status of each requirement + validation_status.push(path_valid && regex_valid); + + // Store notes for each requirement + notes.push(if validation_status.last() == Some(&true) { + req.true_note.clone() + } else { + req.false_note.clone() + }); + } + + if result { + // Check if all requirements are satisfied + if validation_status.iter().all(|&status| status) { break; - } else if $default.is_some() && $buffer.is_empty() { - $buffer = $default.unwrap().to_string(); + } else if self.default.is_some() && buffer.is_empty() { + buffer = self.default.clone().unwrap(); break; } } - if force && $allow_force { + if force && self.allow_force { break; } } - }; -} - -macro_rules! raw_mode_wrapper { - ($content:expr) => { - enable_raw_mode().expect("Failed to enable raw-mode"); - $content; - - disable_raw_mode().expect("Failed to disable raw-mode"); + // clear the line before exit execute!( io::stdout(), - cursor::MoveTo(0, 0), + cursor::MoveTo(0, 4), Clear(ClearType::FromCursorDown), - cursor::DisableBlinking + cursor::Show, ) .unwrap(); - }; -} -/// Validates and returns a string that matches the specified regex pattern. -/// -/// This function prompts the user to enter input and validates the input against the provided -/// regex pattern. It continues to prompt the user until the entered input matches the regex pattern. -/// The function returns the validated input as a string. -/// -/// If `default` is provided and the user enters an empty string, the default value will be used. -/// -/// Note: The `allow_force` option is currently not fully supported due to console issues. See -/// [this issue](https://github.com/crossterm-rs/crossterm/issues/685) for more details. However, -/// users can force input by pressing Shift+Enter. Pressing Shift+Enter will clear the full input. -/// -/// -/// # Arguments -/// -/// * `regex` - A `Regex` object representing the regex pattern to match against the user input. -/// * `default` - An optional default value to be used if the user enters an empty string. -/// * `allow_force` - A boolean indicating whether to allow the user to force input (not fully supported). -/// -/// # Returns -/// -/// A `String` containing the user input that matches the specified regex pattern. -/// -/// # Example -/// -/// ```rust,ignore -/// use regex::Regex; -/// use zenity::menu::input::valid_regex; -/// -/// // Define a regex pattern to match three digits -/// let regex = Regex::new(r"^\d{3}$").unwrap(); -/// -/// // Prompt the user to enter input matching the regex pattern -/// let input = valid_regex(regex, Some("default_value"), false); -/// -/// println!("Valid input: {}", input); -/// ``` -pub fn valid_regex(title: &str, regex: Regex, default: Option<&str>, allow_force: bool) -> String { - let mut buffer = String::new(); - - raw_mode_wrapper!(input_loop!( - title, - buffer, - validate_input(&buffer, ®ex), - default, - allow_force - )); - - buffer -} + Box::new(buffer) + } -/// Validates and returns a `PathBuf` representing the entered path. -/// -/// This function prompts the user to enter a path and validates the input. If the entered path is valid, -/// it returns a `PathBuf` containing the path. Otherwise, it continues prompting the user until a valid -/// path is entered. -/// -/// If `default` is provided and the user enters an empty string, the default value will be used. -/// -/// Note: The `allow_force` option is currently not fully supported due to console issues. See -/// [this issue](https://github.com/crossterm-rs/crossterm/issues/685) for more details. However, -/// users can force input by pressing Shift+Enter. Pressing Shift+Enter will clear the full input. -/// -/// # Arguments -/// -/// * `default` - An optional default value to be used if the user enters an empty string. -/// * `allow_force` - A boolean indicating whether to allow the user to force input (not fully supported). -/// -/// # Returns -/// -/// A `Box` representing the validated path entered by the user. -/// -/// # Examples -/// -/// ```rust,ignore -/// use zenity::menu::input::valid_path; -/// -/// let path = valid_path(Some("/home/user"), true); -/// println!("Entered path: {:?}", path); -/// ``` -pub fn valid_path(title: &str, default: Option<&str>, allow_force: bool) -> Box { - let mut buffer = String::new(); + /// Adds a new requirement to the input. + pub fn add_requirement(mut self, requirement: Requirements) -> Self { + self.requirements.push(requirement); + self + } - raw_mode_wrapper!(input_loop!( - title, - buffer, - validate_path(&buffer), - default, - allow_force - )); + /// Enables the ability to bypass validation requirements and force input submission. + /// This can be triggered by pressing SHIFT + Enter. + /// + /// **Note:** + /// - This feature may not work in all terminal environments. Refer to issue [#685](https://github.com/crossterm-rs/crossterm/issues/685) for more information. + pub fn allow_force(mut self) -> Self { + self.allow_force = true; + self + } - let path = PathBuf::from(buffer); + /// Sets the default value, which can be accepted by pressing Enter. + /// + /// Pressing Enter without typing anything will accept the default value. + pub fn set_default(mut self, default: &str) -> Self { + self.default = Some(default.to_string()); + self + } - Box::new(path) -} + // helper functions: + #[inline] + fn validate_path(path: &str) -> bool { + // useless function but might change something here later... + Path::new(path).exists() + } -#[inline] -fn validate_path(path: &str) -> bool { - // useless function but might change something here later... - Path::new(path).exists() -} + #[inline] + fn validate_regex(buffer: &str, regex: &Regex) -> bool { + if regex.is_match(buffer) { + true + } else { + execute!( + io::stdout(), + cursor::MoveTo(0, 5), + Clear(ClearType::CurrentLine) + ) + .unwrap(); + false + } + } -#[inline] -fn validate_input(buffer: &str, regex: &Regex) -> bool { - if regex.is_match(buffer) { - true - } else { + fn render_input_prompt(&self, buffer: &str, valid: bool, notes: &[Option]) { + // clear the line before rendering execute!( io::stdout(), - cursor::MoveTo(0, 5), - Clear(ClearType::CurrentLine) + cursor::MoveTo(0, 4), + Clear(ClearType::CurrentLine), + cursor::Hide, ) .unwrap(); - false - } -} -fn render_input_prompt(title: &str, buffer: &str, is_valid: &bool, default: Option<&str>) { - execute!( - io::stdout(), - cursor::MoveTo(0, 4), - Clear(ClearType::CurrentLine), - ) - .unwrap(); - if !buffer.is_empty() || default.is_none() { - execute!( - io::stdout(), - Print(title), - cursor::MoveToNextLine(1), - Clear(ClearType::CurrentLine), - if !is_valid { - SetForegroundColor(Color::DarkRed) + // determine color based on validity and color enablement + let (text_color, content) = if !buffer.is_empty() || self.default.is_none() { + let text_color = if *ENABLE_COLOR { + if !valid { + Color::DarkRed + } else { + Color::Green + } } else { - SetForegroundColor(Color::Green) - }, - Print(buffer), - ) - .expect("execute print buffer failed"); - } else { + Color::Reset + }; + (text_color, buffer.to_string()) + } else { + let text_color = if *ENABLE_COLOR { + Color::Grey + } else { + Color::Reset + }; + (text_color, self.default.clone().unwrap_or_default()) + }; + + // render the prompt execute!( io::stdout(), - Print(title), + Print(&self.title), cursor::MoveToNextLine(1), Clear(ClearType::CurrentLine), - SetForegroundColor(Color::Grey), - Print(default.unwrap()), - Print(" (Default)"), + SetForegroundColor(text_color), + Print(content), ) - .expect("execute print default failed"); - } - execute!(io::stdout(), SetForegroundColor(Color::Reset),).unwrap(); -} - -#[cfg(test)] -mod tests { - use super::*; + .unwrap(); - #[test] - fn test_validate_path_existing_file() { - // Create a temporary file for testing - let file_path = "test_file.txt"; - std::fs::File::create(file_path).expect("Failed to create file"); + // if using default, indicate it + if self.default.is_some() && buffer.is_empty() { + execute!(io::stdout(), Print(" (Default)")).unwrap(); + } - // Validate the path of the temporary file - assert!(validate_path(file_path)); + // reset color + execute!( + io::stdout(), + SetForegroundColor(Color::Reset), + cursor::SavePosition, + cursor::MoveToNextLine(2) + ) + .unwrap(); - // Delete the temporary file - std::fs::remove_file(file_path).expect("Failed to delete file"); - } + if *ENABLE_COLOR { + execute!(io::stdout(), SetForegroundColor(Color::DarkGrey)).unwrap(); + } - #[test] - fn test_validate_path_nonexistent_file() { - // Create a temporary file path that doesn't exist - let file_path = "nonexistent_file.txt"; + // Print notes + for note in notes.iter() { + match note { + Some(note_str) => { + execute!( + io::stdout(), + cursor::MoveToNextLine(1), + Print("- "), + Print(note_str) + ) + .unwrap(); + } + None => { + execute!( + io::stdout(), + cursor::MoveToNextLine(1), + Clear(ClearType::CurrentLine), + cursor::MoveToPreviousLine(1), + ) + .unwrap(); + } + } + } - // Validate the path of the nonexistent file - assert!(!validate_path(file_path)); - } + if self.allow_force && !buffer.is_empty() && !valid { + execute!( + io::stdout(), + cursor::MoveToNextLine(2), + Print("Press SHIFT + Enter to force input"), + ) + .unwrap(); + } else { + execute!( + io::stdout(), + cursor::MoveToNextLine(2), + Clear(ClearType::CurrentLine), + cursor::MoveToNextLine(2), + ) + .unwrap(); + } - #[test] - fn test_render_input_prompt() { - // Call the render_input_prompt function with a mock Stdout - render_input_prompt("Title", "123", &true, Some("Default stuff")); - } + if self.default.is_some() && buffer.is_empty() { + execute!( + io::stdout(), + cursor::MoveToNextLine(2), + Print("Press Enter to accept default"), + ) + .unwrap(); + } else { + execute!( + io::stdout(), + cursor::MoveToNextLine(2), + Clear(ClearType::CurrentLine), + cursor::MoveToNextLine(2), + ) + .unwrap(); + } - #[test] - fn test_validate_input() { - // Call the render_input_prompt function with a mock Stdout - assert!(validate_input("123", &Regex::new(r"^\d{3}$").unwrap())); - assert!(!validate_input("abc", &Regex::new(r"^\d{3}$").unwrap())); + // reset color + execute!( + io::stdout(), + cursor::RestorePosition, + SetForegroundColor(Color::Reset), + cursor::Show, + ) + .unwrap(); } } diff --git a/src/terminal.rs b/src/terminal.rs index 30bc87d..0ce4acb 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -6,6 +6,18 @@ pub(crate) mod console_render { use crate::color::ENABLE_COLOR; use crate::style::StyledString; + macro_rules! raw_mode_wrapper { + ($content:expr) => { + enable_raw_mode().expect("Failed to enable raw-mode"); + + $content; + + disable_raw_mode().expect("Failed to disable raw-mode"); + }; + } + + pub(crate) use raw_mode_wrapper; + pub fn render_line(frame: &Vec, row: u16) { let mut stdout = stdout(); queue!( @@ -105,7 +117,6 @@ pub(crate) mod console_cursor { #[cfg(test)] mod tests { use crate::style::Color; - use crate::style::StyledString; use super::*;