onion_vault_cli 0.0.3

OnionVault CLI
Documentation
pub mod onion_vault;
pub use onion_vault::{
    identity_group,
    account,
    credential,
};



use crate::app::{
    APP_STATES_FOR_MENU,
    AppState,
};
use crate::common::constants::*;
use crate::re_export::std_anyhow::*;




use enigo::{
    Direction::{Click, Press, Release},
    Enigo,
    Key,
    Keyboard,
    Settings,
};




use std::default::Default;
use std::process::{Command, Stdio};
use std::io::Write;
use std::time::Duration;
use std::thread;






pub struct EnterString {
    prompt: String,
    default_result: String,
}

impl Default for EnterString {
    fn default() -> Self {
        EnterString {
            prompt: String::new(),
            default_result: String::from("EXIT"),
        }
    }
}

impl EnterString {
    pub fn prompt(mut self, prompt: &str) -> Self {
        self.prompt = prompt.to_string();

        self
    }


    pub fn default_result(mut self, default_result: &str) -> Self {
        self.default_result = default_result.to_string();

        self
    }

    pub fn enter_with_confirm_no_exit(&self) -> String {
        let res = loop {
            let res = Input::<String>::with_theme(&ColorfulTheme::default())
                .with_prompt(format!(
                        "{}\nDefault:",
                        self.prompt.clone())
                )
                .default(self.default_result.clone())
                .interact_text()
                .unwrap_or_default();

            println!("\nYou Entered \"{}\", sure?", style(&res)
                .underlined()
                .bold()
                .white()
                .on_color256(13)
            );

            let ok = Confirm::with_theme(&ColorfulTheme::default())
                .with_prompt("type 'y' or 'n'")
                .interact()
                .unwrap_or(false);

            if ok { break res }
            print!("\n\n\n\n");
        };

        res
    }

    pub fn enter_with_confirm(&self) -> Option<String> {
        let res = loop {
            let res = Input::<String>::with_theme(&ColorfulTheme::default())
                .with_prompt(format!(
                        "{}\n(Or Enter \"EXIT\" to go back to the previous step).\nDefault:",
                        self.prompt.clone())
                )
                .default(self.default_result.clone())
                .interact_text()
                .unwrap_or_default();

            println!("\nYou Entered \"{}\", sure?", style(&res)
                .underlined()
                .bold()
                .white()
                .on_color256(13)
            );

            let ok = Confirm::with_theme(&ColorfulTheme::default())
                .with_prompt("type 'y' or 'n'")
                .interact()
                .unwrap_or(false);

            if ok { break res }
            print!("\n\n\n\n");
        };

        if res.eq("EXIT") {
            None
        } else {
            Some(res)
        }
    }
}



pub fn ask_bool() -> bool {
    let res = Confirm::with_theme(&ColorfulTheme::default())
        .interact()
        .unwrap_or(true);

    res
}


pub fn ask_for_retry() -> bool {
    let res = ask_bool();

    print!("\n\n\n");

    res
}




pub fn choose_what_to_do<T: ToString>(option_str: &[T]) -> usize {
    FuzzySelect::with_theme(&ColorfulTheme::default())
        .with_prompt("Choose what to do")
        .items(&option_str)
        .interact()
        .unwrap_or(0)
}



fn switch_window(enigo: &mut Enigo) {
    if let Err(e) = (|| -> anyhow::Result<()> {
        if cfg!(target_os = "windows") || cfg!(target_os = "linux") {
            let _ = enigo.key(Key::Alt, Press);
            let _ = enigo.key(Key::Tab, Click);
            let _ = enigo.key(Key::Alt, Release);
        } else if cfg!(target_os = "macos") {
            let _ = enigo.key(Key::Meta, Press);
            let _ = enigo.key(Key::Tab, Click);
            let _ = enigo.key(Key::Meta, Release);
        }
        Ok(())
    })() {
        eprintln!("Error executing xdotool: {}", e);
    }
}


pub fn enigo_fill_password(mut password: &mut Zeroizing<String>) {
    remove_trailing_newlines(&mut password);

    let password_ref: &str = password.as_ref();
    if let Err(e) = (|| -> anyhow::Result<()> {
        let mut enigo = Enigo::new(&Settings::default())?;
        switch_window(&mut enigo);
        thread::sleep(Duration::from_millis(150));
        enigo.text(password_ref)?;

        Ok(())
    })() {
        eprintln!("Error executing enigo: {}", e);
    }

    password.zeroize();
}



#[allow(dead_code)]
pub fn xdotool_fill_password(mut password: &mut Zeroizing<String>) {

    remove_trailing_newlines(&mut password);
    if let Err(e) = (|| -> anyhow::Result<()> {
        let mut child = Command::new("sh")
            .arg("-c")
            .arg("xdotool type --delay 50 --file -")
            .env("XDOTOOL_DEBUG", "0")
            .stdin(Stdio::piped())
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .spawn()?;

        let mut stdin = child.stdin.take()
            .ok_or(Error::msg("stdin.take() == None"))?;

        stdin.write_all(password.as_bytes())?;
        drop(stdin);

        let _status = child.wait()?;

        Ok(())
    })() {
        eprintln!("Error executing xdotool: {}", e);
    }
    password.zeroize();
}

fn remove_trailing_newlines(s: &mut String) {
    s.truncate(s.trim_end_matches(&['\r', '\n'][..]).len());
}


pub fn creat_new_password() -> String {
    println!("{}", style("Do you want Auto-generate Strong Password?")
        .white()
        .on_color256(57)
    );
    let auto_generat_strong_password = ask_bool();

    if auto_generat_strong_password {
        let max_password_length: u8 = Input::with_theme(&ColorfulTheme::default())
            .with_prompt("Please Enter Max Password Length")
            .interact_text()
            .unwrap_or(37);

        passwords::PasswordGenerator::new()
            .length(max_password_length.into())
            .numbers(true)
            .lowercase_letters(true)
            .uppercase_letters(true)
            .symbols(true)
            .spaces(false)
            .exclude_similar_characters(true)
            .strict(true)
            .generate_one()
            .unwrap_or("EXIT".to_string())
    } else {
        dialoguer::Password::with_theme(&ColorfulTheme::default())
            .with_prompt("Password")
            .with_confirmation("Repeat password", "Error: the passwords don't match.")
            .interact()
            .unwrap_or("EXIT".to_string())
    }
}


pub fn exit_process() {
    println!("TO BE EXIT :)");
    std::process::exit(0);

}



pub fn print_error(e: Error) {
    println!("{}", error_color(e));
}

pub fn error_color(e: Error) -> StyledObject<String> {
    style(e.to_string()).white().on_red()
}



pub fn print_warn(prompt: &str) {
    println!("{}", warn_color(prompt));
}

pub fn warn_color(prompt: &str) -> StyledObject<&str> {
    style(prompt).yellow().on_color256(54)
}

pub fn check_environment_safety() -> bool {
    print_warn(SECURITY_CONFIDENCE_PROMPT);
    let res = Confirm::with_theme(&ColorfulTheme::default())
        .interact()
        .unwrap_or(false);

    res
}

pub fn fuzzy_select_menu() -> AppState {
    let options: Vec<AppState> = APP_STATES_FOR_MENU.into_iter().collect();

    let selection = FuzzySelect::with_theme(&ColorfulTheme::default())
        .with_prompt("Choose what to do")
        .items(&options)
        .interact()
        .unwrap_or(0);

    options[selection].clone()
}