promptuity 0.0.5

Promptuity is a library that provides interactive prompts.
Documentation
use promptuity::prompts::{
    Input, MultiSelect, MultiSelectOption, Number, Password, Select, SelectOption,
};
use promptuity::style::{Color, Styled, Symbol};
use promptuity::themes::FancyTheme;
use promptuity::{Error, Promptuity, Term};

const ERR_REQUIRED: &str = "Required!";
const S_SELECT_CURSOR: Symbol = Symbol("", " > ");
const S_SELECTED: Symbol = Symbol("", "[x]");
const S_UNSELECTED: Symbol = Symbol(" ", "[ ]");

struct InputFormatter;
impl promptuity::prompts::InputFormatter for InputFormatter {
    fn err_required(&self) -> String {
        ERR_REQUIRED.to_string()
    }
}

struct PasswordFormatter;
impl promptuity::prompts::PasswordFormatter for PasswordFormatter {
    fn err_required(&self) -> String {
        ERR_REQUIRED.to_string()
    }
}

struct NumberFormatter;
impl promptuity::prompts::NumberFormatter for NumberFormatter {
    fn err_required(&self) -> String {
        ERR_REQUIRED.to_string()
    }

    fn err_invalid_format(&self) -> String {
        "Invalid!".into()
    }

    fn err_invalid_range(&self, min: isize, max: isize) -> String {
        format!("Invalid Range! (expect min={}, max={})", min, max)
    }
}

struct SelectFormatter;
impl promptuity::prompts::SelectFormatter for SelectFormatter {
    fn option_icon(&self, active: bool) -> String {
        if active {
            Styled::new(S_SELECT_CURSOR).fg(Color::Cyan).to_string()
        } else {
            Styled::new(S_UNSELECTED).fg(Color::DarkGrey).to_string()
        }
    }

    fn option_label(&self, label: String, active: bool) -> String {
        if active {
            Styled::new(label)
                .fg(Color::Magenta)
                .underline()
                .to_string()
        } else {
            label
        }
    }

    fn option_hint(&self, hint: Option<String>, active: bool) -> String {
        if active {
            hint.as_ref().map_or_else(String::new, |hint| {
                format!(
                    " {}",
                    Styled::new(format!("({})", hint)).fg(Color::DarkGrey)
                )
            })
        } else {
            String::new()
        }
    }

    fn option(&self, icon: String, label: String, hint: String, _active: bool) -> String {
        format!("{} {}{}", icon, label, hint)
    }
}

struct MultiSelectFormatter;
impl promptuity::prompts::MultiSelectFormatter for MultiSelectFormatter {
    fn option_icon(&self, active: bool, selected: bool) -> String {
        if active {
            Styled::new(S_SELECT_CURSOR).fg(Color::Cyan).to_string()
        } else if selected {
            Styled::new(S_SELECTED).fg(Color::Cyan).to_string()
        } else {
            Styled::new(S_UNSELECTED).fg(Color::DarkGrey).to_string()
        }
    }

    fn option_label(&self, label: String, active: bool, _selected: bool) -> String {
        if active {
            Styled::new(label)
                .fg(Color::Magenta)
                .underline()
                .to_string()
        } else {
            label
        }
    }

    fn option_hint(&self, hint: Option<String>, active: bool, selected: bool) -> String {
        if active || selected {
            hint.as_ref().map_or_else(String::new, |hint| {
                format!(
                    " {}",
                    Styled::new(format!("({})", hint)).fg(Color::DarkGrey)
                )
            })
        } else {
            String::new()
        }
    }

    fn option(
        &self,
        icon: String,
        label: String,
        hint: String,
        _active: bool,
        _selected: bool,
    ) -> String {
        format!("{} {}{}", icon, label, hint)
    }

    fn submit(&self, labels: Vec<String>) -> String {
        labels.join(" / ")
    }

    fn err_required(&self) -> String {
        ERR_REQUIRED.to_string()
    }
}

fn main() -> Result<(), Error> {
    let mut term = Term::default();
    let mut theme = FancyTheme::default();
    let mut p = Promptuity::new(&mut term, &mut theme);

    p.term().clear()?;

    p.with_intro("Custom Format").begin()?;

    let _ = p.prompt(Input::new("Input message").with_formatter(InputFormatter))?;

    let _ = p.prompt(
        Password::new("Password message")
            .with_formatter(PasswordFormatter)
            .with_mask(''),
    )?;

    let _ = p.prompt(
        Number::new("Number message")
            .with_formatter(NumberFormatter)
            .with_min(0)
            .with_max(10),
    )?;

    let _ = p.prompt(
        Select::new(
            "Select message",
            vec![
                SelectOption::new("Option1", "option1"),
                SelectOption::new("Option2", "option2").with_hint("Hint!"),
                SelectOption::new("Option3", "option3"),
            ],
        )
        .with_formatter(SelectFormatter),
    )?;

    let _ = p.prompt(
        MultiSelect::new(
            "Select message",
            vec![
                MultiSelectOption::new("Option1", "option1"),
                MultiSelectOption::new("Option2", "option2").with_hint("Hint!"),
                MultiSelectOption::new("Option3", "option3"),
            ],
        )
        .with_formatter(MultiSelectFormatter),
    )?;

    p.with_outro("Finish!").finish()?;

    Ok(())
}