bat-cli 0.12.1

Blockchain Auditor Toolkit (BAT)
use colored::Colorize;
use dialoguer::{console::Term, theme::ColorfulTheme, FuzzySelect, Input, MultiSelect, Select};
use error_stack::{IntoReport, Result, ResultExt};

use crate::commands::CommandError;

#[derive(Debug, Clone)]
pub struct BatDialoguer;

impl BatDialoguer {
    pub fn multiselect<T>(
        prompt_text: String,
        items: Vec<T>,
        default: Option<&Vec<bool>>,
        force_select: bool,
    ) -> Result<Vec<usize>, CommandError>
    where
        T: ToString + Clone,
    {
        let waiting_response = true;
        while waiting_response {
            let colorful_theme = &ColorfulTheme::default();
            let multi_select = MultiSelect::with_theme(colorful_theme);
            let mut dialog = multi_select.with_prompt(&prompt_text).items(&items);

            if let Some(def) = default {
                dialog = dialog.defaults(def);
            }
            let result = dialog
                .interact_on_opt(&Term::stderr())
                .into_report()
                .change_context(CommandError)?
                .ok_or(CommandError)
                .into_report()?;
            log::debug!("force_select: {}", force_select);
            log::debug!("multiselect_result:\n{:#?}", result);
            if force_select && result.is_empty() {
                println!(
                    "{}",
                    "No option selected, you should pick at least 1 by hitting space bar".red()
                );
            } else {
                return Ok(result);
            }
        }
        Ok(vec![])
    }

    pub fn select<T>(
        prompt_text: String,
        items: Vec<T>,
        default: Option<usize>,
    ) -> Result<usize, CommandError>
    where
        T: ToString + Clone,
    {
        let colorful_theme = &ColorfulTheme::default();
        let select = Select::with_theme(colorful_theme);
        let mut dialog = select.with_prompt(&prompt_text).items(&items);

        if let Some(def) = default {
            dialog = dialog.default(def);
        } else {
            dialog = dialog.default(0);
        }

        dialog
            .interact_on_opt(&Term::stderr())
            .into_report()
            .change_context(CommandError)?
            .ok_or(CommandError)
            .into_report()
    }

    pub fn fuzzy_select<T>(prompt_text: String, items: Vec<T>) -> Result<usize, CommandError>
    where
        T: ToString + Clone,
    {
        let colorful_theme = &ColorfulTheme::default();
        FuzzySelect::with_theme(colorful_theme)
            .with_prompt(&prompt_text)
            .items(&items)
            .default(0)
            .interact_on_opt(&Term::stderr())
            .into_report()
            .change_context(CommandError)?
            .ok_or(CommandError)
            .into_report()
    }

    pub fn select_yes_or_no(prompt_text: String) -> Result<bool, CommandError> {
        let colorful_theme = &ColorfulTheme::default();
        let select = Select::with_theme(colorful_theme);
        let dialog = select
            .with_prompt(&prompt_text)
            .item("yes")
            .item("no")
            .default(0);
        let opt = dialog
            .interact_on_opt(&Term::stderr())
            .into_report()
            .change_context(CommandError)?
            .ok_or(CommandError)
            .into_report()?;

        Ok(opt == 0)
    }

    pub fn input(prompt_text: String) -> Result<String, CommandError> {
        let colorful_theme = &ColorfulTheme::default();
        let input = Input::with_theme(colorful_theme);
        let dialog: String = input
            .with_prompt(&prompt_text)
            .interact_text()
            .into_report()
            .change_context(CommandError)?;

        Ok(dialog)
    }

    pub fn input_with_default(
        prompt_text: String,
        default: String,
    ) -> Result<String, CommandError> {
        let colorful_theme = &ColorfulTheme::default();
        let dialog: String = Input::with_theme(colorful_theme)
            .with_prompt(&prompt_text)
            .default(default)
            .interact_text()
            .into_report()
            .change_context(CommandError)?;

        Ok(dialog)
    }
}

pub fn multiselect<T>(
    prompt_text: &str,
    items: Vec<T>,
    default: Option<&Vec<bool>>,
) -> Result<Vec<usize>, CommandError>
where
    T: ToString + Clone,
{
    BatDialoguer::multiselect(prompt_text.to_string(), items, default, false)
}

pub fn select<T>(
    prompt_text: &str,
    items: Vec<T>,
    default: Option<usize>,
) -> Result<usize, CommandError>
where
    T: ToString + Clone,
{
    BatDialoguer::select(prompt_text.to_string(), items, default)
}

pub fn select_yes_or_no(prompt_text: &str) -> Result<bool, CommandError> {
    BatDialoguer::select_yes_or_no(prompt_text.to_string())
}

pub fn input(prompt_text: &str) -> Result<String, CommandError> {
    BatDialoguer::input(prompt_text.to_string())
}

pub fn input_with_default(prompt_text: &str, default: &str) -> Result<String, CommandError> {
    BatDialoguer::input_with_default(prompt_text.to_string(), default.to_string())
}