entrust 0.5.1

A CLI password manager
Documentation
use crate::theme::{DIALOG_THEME, chevron_prompt};
use entrust_core::get_existing_locations;
use entrust_dialog::dialog::Dialog;
use entrust_dialog::input::InputDialog;
use entrust_dialog::input::confirmation::Confirmation;
use entrust_dialog::input::mask::InputMask;
use entrust_dialog::input::prompt::Prompt;
use entrust_dialog::input::validator::{Validator, ValidatorFn, combine, validate_filename};
use entrust_dialog::select::SelectDialog;
use std::borrow::Cow;
use std::ops::Deref;
use std::path::Path;

pub fn select_existing_key(store: &Path) -> anyhow::Result<String> {
    let vec = get_existing_locations(store)?
        .files
        .into_iter()
        .map(Cow::Owned)
        .collect();
    let selected = SelectDialog::new(vec).run()?;
    match selected {
        None => select_existing_key(store),
        Some(sel) => Ok(sel.to_string()),
    }
}

pub fn read_password_interactive(initial: &str) -> anyhow::Result<String> {
    let mask = if initial.is_empty() {
        InputMask::dots()
    } else {
        InputMask::none()
    };
    let mut dialog = InputDialog::default()
        .with_content(initial)
        .with_prompt(Prompt::inline(chevron_prompt!("Enter new password")))
        .with_validator(Validator::not_empty("The password must not be empty."))
        .with_mask(mask)
        .with_theme(DIALOG_THEME.deref());
    if initial.is_empty() {
        dialog = dialog.with_confirmation(match_confirmation())
    }
    let pass = dialog.run()?;
    Ok(pass)
}

fn match_confirmation() -> Confirmation<'static> {
    Confirmation::new(Prompt::inline(chevron_prompt!("Confirm password  ")))
        .with_validation_message("The entered passwords do not match.")
}

pub fn read_new_key_interactive(prompt: &'static str, store: &Path) -> anyhow::Result<String> {
    let existing = get_existing_locations(store)?;
    let validator = Validator::new(combine(
        validate_filename(true),
        validate_new_key(existing.files),
    ));
    let new_key = InputDialog::default()
        .with_prompt(Prompt::inline(prompt))
        .with_validator(validator)
        .with_completions(existing.dirs.into_iter().map(Cow::Owned).collect())
        .with_theme(DIALOG_THEME.deref())
        .run()?;
    Ok(new_key)
}

fn validate_new_key(existing_files: Vec<String>) -> impl ValidatorFn<'static> {
    move |chars| {
        if existing_files.contains(&chars.iter().collect()) {
            Some("Key already exists".into())
        } else {
            None
        }
    }
}