#![crate_type = "lib"]
#![allow(clippy::borrowed_box)]
use help::CliHelpScreen;
pub use indexmap::{indexmap, IndexMap};
pub use textwrap::Options as Textwrap_Options;
pub use textwrap::fill as Textwrap_Fill;
use router::CliRouter;
use rpassword::read_password;
use std::collections::HashMap;
pub use std::io;
pub use std::io::Write;
use zxcvbn::zxcvbn;
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");
}
#[macro_export]
macro_rules! cli_send {
($text:expr) => {
let wrapped_text = Textwrap_Fill($text, Textwrap_Options::new(75));
print!("{}", wrapped_text);
io::stdout().flush().unwrap();
};
($text:expr, $( $arg:expr ),*) => {
let mut args = vec![];
$( args.push($arg.to_string()); )*
let mut text: String = $text.to_string();
for arg in args {
text = text.replacen("{}", arg.to_string().as_str(), 1);
}
let wrapped_text = Textwrap_Fill(text.as_str(), Textwrap_Options::new(75));
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");
}
#[macro_export]
macro_rules! cli_error {
($text:expr) => {
let wrapped_text = Textwrap_Fill(format!("ERROR: {}", $text).as_str(), Textwrap_Options::new(75));
print!("{}\r\n", wrapped_text);
io::stdout().flush().unwrap();
};
($text:expr, $( $arg:expr ),*) => {
let mut args = vec![];
$( args.push($arg.to_string()); )*
let mut text: String = $text.to_string();
for arg in args {
text = text.replacen("{}", arg.to_string().as_str(), 1);
}
let wrapped_text = Textwrap_Fill(format!("ERROR: {}", text).as_str(), Textwrap_Options::new(75));
print!("{}\r\n", wrapped_text);
io::stdout().flush().unwrap();
};
}
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");
}