#![crate_type = "lib"]
#![allow(clippy::borrowed_box)]
use std::io::Write;
use std::io;
use rpassword::read_password;
use zxcvbn::zxcvbn;
use std::collections::HashMap;
pub use indexmap::{IndexMap, indexmap};
use help::CliHelpScreen;
use router::CliRouter;
pub mod help;
pub mod router;
pub trait CliCommand {
fn process(&self, args: Vec<String>, flags: Vec<String>, value_flags: HashMap<String, String>);
fn help(&self) -> CliHelpScreen;
}
pub fn cli_run(router: &CliRouter) {
let (cmd, req) = router.lookup();
if req.is_help {
CliHelpScreen::render(cmd, &req.cmd_alias, &req.shortcuts);
} else {
cmd.process(req.args, req.flags, req.value_flags);
}
}
pub fn cli_header(text: &str) {
println!("------------------------------");
println!("-- {}", text);
println!("------------------------------\n");
}
pub fn cli_send(text: &str) {
let options = textwrap::Options::new(75);
let wrapped_text = textwrap::fill(text, options);
print!("{}", wrapped_text);
io::stdout().flush().unwrap();
}
pub fn cli_get_option(question: &str, options: &IndexMap<String, String>) -> String {
let message = format!("{}\r\n\r\n", question);
cli_send(&message);
for (key, value) in options.iter() {
let line = format!(" [{}] {}\r\n", key, value);
cli_send(&line);
}
cli_send("\r\nSelect One: ");
let mut input: String;
loop {
input = String::new();
io::stdin().read_line(&mut input).expect("Failed to read line");
let input = input.trim();
if !options.contains_key(input) {
print!("\r\nInvalid option, try again: ");
io::stdout().flush().unwrap();
} else {
break;
}
}
input
}
pub fn cli_get_input(message: &str, default_value: &str) -> String {
cli_send(message);
io::stdout().flush().unwrap();
let mut input = String::new();
io::stdin().read_line(&mut input).expect("Failed to read line");
let mut input = input.trim();
if input.trim().is_empty() {
input = default_value;
}
String::from(input)
}
pub fn cli_confirm(message: &str) -> bool {
let confirm_message = format!("{} (y/n): ", message);
cli_send(&confirm_message);
let mut _input = "".to_string();
loop {
_input = String::new();
io::stdin().read_line(&mut _input).expect("Failed to read line");
let _input = _input.trim().to_lowercase();
if _input != "y" && _input != "n" {
cli_send("Invalid option, please try again. Enter (y/n): ");
} else {
break;
}
}
let res_char = _input.chars().next().unwrap();
res_char == 'y'
}
pub fn cli_get_password(message: &str) -> String {
let password_message = if message.is_empty() { "Password: " } else { message };
let mut _password = String::new();
loop {
cli_send(password_message);
_password = read_password().unwrap();
if _password.is_empty() {
cli_send("You did not specify a password");
} else {
break;
}
}
_password
}
pub fn cli_get_new_password(req_strength: u8) -> String {
let mut _password = String::new();
let mut _confirm_password = String::new();
loop {
cli_send("Desired Password: ");
_password = read_password().unwrap();
if _password.is_empty() {
cli_send("You did not specify a password");
continue;
}
let strength = zxcvbn(&_password, &[]).unwrap();
if strength.score() < req_strength {
cli_send("Password is not strong enough. Please try again.\n\n");
continue;
}
cli_send("Confirm Password: ");
_confirm_password = read_password().unwrap();
if _password != _confirm_password {
cli_send("Passwords do not match, please try again.\n\n");
continue;
}
break;
}
_password
}
pub fn cli_display_table(columns: Vec<&str>, rows: Vec<Vec<&str>>) {
if rows.is_empty() {
cli_send("No rows to display.\n\n");
return;
}
let mut sizes: HashMap<&str, usize> = HashMap::new();
for col in columns.as_slice() {
sizes.insert(col, 0);
}
for _row in rows.clone() {
for col in columns.as_slice() {
let num: usize = col.len();
if num > sizes[col] {
sizes.insert(col, num + 3);
}
}
}
let mut header = String::from("+");
let mut col_header = String::from("|");
for col in columns.clone() {
let padded_col = format!("{}{}", col, " ".repeat(sizes[col] - col.len()));
header = header + "-".repeat(sizes[col] + 1).as_str() + "+";
col_header += format!(" {}|", padded_col).as_str();
}
println!("{}\n{}\n{}", header, col_header, header);
for row in rows {
let mut line = String::from("|");
for (i, val) in row.into_iter().enumerate() {
let padded_val = format!(" {}{}", val, " ".repeat(sizes[columns[i]] - val.len()));
line += format!("{}|", padded_val).as_str();
}
println!("{}", line);
}
println!("{}\n", header);
}
pub fn cli_display_array(rows: &IndexMap<String, String>) {
let mut size = 0;
for key in rows.keys() {
if key.len() + 8 > size {
size = key.len() + 8;
}
}
let indent = " ".repeat(size);
let indent_size = size - 4;
for (key, value) in rows {
let left_col = format!(" {}{}", key, " ".repeat(indent_size - key.len()));
let options = textwrap::Options::new(75).initial_indent(&left_col).subsequent_indent(&indent);
let line = textwrap::fill(value, &options);
println!("{}", line);
}
cli_send("\r\n");
}
pub fn cli_error(message: &str) {
let err_message = format!("Error: {}\n\n", message);
cli_send(&err_message);
std::process::exit(1);
}
pub fn cli_success (message: &str, indented_lines: Vec<&str>) {
cli_send(message);
cli_send("\r\n");
for line in indented_lines {
println!(" {}", line);
}
cli_send("\r\n");
}
pub fn cli_clear_screen() {
print!("\x1B[2J");
}