1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
//! A simple library for console-based user input and option selection.
use console::{style, Key, Term};
use std::io::{self, Write};
/// Reads user input from the console.
///
/// This function prompts the user with a message (`before`) and reads a line of input from the
/// console. The input can be empty unless `allow_empty` is set to `false`. If `new_line` is set
/// to `true`, a newline character will be printed after the prompt.
///
/// # Arguments
///
/// * `before` - The text to display before prompting for input.
/// * `allow_empty` - If true, allows the input to be empty.
/// * `new_line` - If true, adds a newline character after the prompt.
///
/// # Returns
///
/// Returns an `Option<String>` containing the user's input or `None` if the input is empty and
/// `allow_empty` is `false`.
///
/// # Example
///
/// ```no_run
/// use console_utils::input;
///
/// let user_input = input("Enter something: ", false, false);
///
/// match user_input {
/// Some(value) => println!("You entered: {}", value),
/// None => println!("Input is empty."),
/// }
/// ```
pub fn input(before: &str, allow_empty: bool, new_line: bool) -> Option<String> {
loop {
print!("{before} {}", if new_line { '\n' } else { '\0' });
io::stdout().flush().unwrap();
let mut cli = String::new();
io::stdin().read_line(&mut cli).unwrap();
if allow_empty && cli.trim().is_empty() {
return None;
} else if !cli.trim().is_empty() {
return Some(cli.trim().to_owned());
} else {
println!("\nWrong Input\n");
}
}
}
/// Allows the user to select options from a list using the console.
///
/// This function displays a list of options with checkboxes. The user can navigate through the
/// options using arrow keys or 'w' and 's' keys. Pressing the spacebar toggles the selection of
/// the current option. If the user presses Enter, the function returns a vector of booleans
/// indicating which options were selected.
///
/// # Arguments
///
/// * `before` - The text to display before the list of options.
/// * `options` - A vector of strings representing the available options.
/// * `allow_empty` - If true, allows the user to exit without selecting any option.
/// * `multiple` - If true, allows the user to select multiple options.
///
/// # Returns
///
/// Returns an `Option<Vec<bool>>` containing a vector of booleans indicating which options were
/// selected. Returns `None` if no option was selected and `allow_empty` is `false`.
///
/// # Example
///
/// ```no_run
/// use console_utils::select;
///
/// let options = vec![
/// "Option 1",
/// "Option 2",
/// "Option 3",
/// ];
///
/// let selected_indices = select("Select an option:", &options, false, false);
///
/// match selected_indices {
/// Some(indices) => {
/// println!("Selected indices: {:?}", indices);
/// }
/// None => {
/// println!("No option selected.");
/// }
/// }
/// ```
pub fn select(
before: &str,
options: &[&str],
allow_empty: bool,
multiple: bool,
) -> Option<Vec<bool>> {
loop {
let mut matrix: Vec<bool> = vec![];
let stdout = Term::buffered_stdout();
println!("{}\n", before,);
for i in options {
println!("[ ] {}", i);
matrix.push(false);
}
stdout.move_cursor_up(options.len()).unwrap();
stdout.flush().unwrap();
let mut i = 0;
loop {
if let Ok(character) = stdout.read_key() {
match character {
Key::ArrowUp | Key::Char('w') => {
if i > 0 {
stdout.move_cursor_up(1).unwrap();
i -= 1;
}
}
Key::ArrowDown | Key::Char('s') => {
if i < options.len() - 1 {
stdout.move_cursor_down(1).unwrap();
i += 1;
}
}
Key::Char(' ') => {
stdout.clear_line().unwrap();
if matrix[i] {
stdout.write_line(&format!("[ ] {}", options[i])).unwrap();
matrix[i] = false;
} else {
stdout
.write_line(&format!("[{}] {}", style("*").cyan(), options[i]))
.unwrap();
matrix[i] = true;
}
stdout.move_cursor_up(1).unwrap();
stdout.flush().unwrap();
}
Key::Enter => {
break;
}
_ => {}
}
}
stdout.flush().unwrap();
}
if matrix.iter().filter(|&&selected| selected).count() > 1 && !multiple {
reset(stdout, "\nPlease Select only one!\n", options.len());
} else if allow_empty && matrix.iter().all(|&x| !x) {
reset(stdout, "", options.len());
return None;
} else if !matrix.iter().all(|&x| !x) {
reset(stdout, "", options.len());
return Some(matrix);
} else {
reset(stdout, "\nWrong Input\n", options.len());
}
}
}
// Internal function for resetting the console.
fn reset(stdout: Term, mes: &str, len: usize) {
stdout.move_cursor_down(len).unwrap();
stdout.flush().unwrap();
println!("{mes}");
}