use console::Emoji;
pub static ACTION: Emoji<'_, '_> = Emoji("🚀 ", ">> ");
pub static SEARCH: Emoji<'_, '_> = Emoji("🔍 ", "> ");
pub static CHECK: Emoji<'_, '_> = Emoji("✅ ", "√ ");
pub static SUCCESS: Emoji<'_, '_> = Emoji("🎉 ", "* ");
pub static SUCCESS_CREATE: Emoji<'_, '_> = Emoji("✨ ", "+ ");
pub static SUCCESS_UPDATE: Emoji<'_, '_> = Emoji("🔄 ", "~ ");
pub static WARN: Emoji<'_, '_> = Emoji("⚠️ ", "! ");
pub static ERROR: Emoji<'_, '_> = Emoji("❌ ", "x ");
pub static INFO: Emoji<'_, '_> = Emoji("💡 ", "i ");
pub static SPARKLE: Emoji<'_, '_> = Emoji("✨", "");
pub static MEMO: Emoji<'_, '_> = Emoji("📝", "");
use anyhow::Result;
pub trait Ui: Send + Sync {
fn input(&self, prompt: &str, default: Option<String>, allow_empty: bool) -> Result<String>;
fn confirm(&self, prompt: &str, default: bool) -> Result<bool>;
fn password(&self, prompt: &str, confirm: Option<&str>) -> Result<String>;
fn select(&self, prompt: &str, items: &[&str], default: usize) -> Result<usize>;
fn print_info(&self, msg: &str);
fn print_success(&self, msg: &str);
fn print_error(&self, msg: &str);
fn print_warn(&self, msg: &str);
}
pub fn create_progress_bar(len: u64, msg: &str) -> indicatif::ProgressBar {
let pb = indicatif::ProgressBar::new(len);
pb.set_style(
indicatif::ProgressStyle::default_bar()
.template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta}) {msg}")
.expect("Invalid progress bar template format")
.progress_chars("#>-"),
);
pb.set_message(msg.to_string());
pb
}
pub fn create_spinner(msg: &str) -> indicatif::ProgressBar {
let pb = indicatif::ProgressBar::new_spinner();
pb.enable_steady_tick(std::time::Duration::from_millis(120));
pb.set_style(
indicatif::ProgressStyle::default_spinner()
.template("{spinner:.green} {msg}")
.expect("Invalid spinner template format"),
);
pb.set_message(msg.to_string());
pb
}
pub struct DialoguerUi {
pub term: Option<console::Term>,
}
impl DialoguerUi {
pub fn new() -> Self {
Self { term: None }
}
pub fn with_term(term: console::Term) -> Self {
Self { term: Some(term) }
}
}
impl Default for DialoguerUi {
fn default() -> Self {
Self::new()
}
}
impl Ui for DialoguerUi {
fn input(&self, prompt: &str, default: Option<String>, allow_empty: bool) -> Result<String> {
let input = dialoguer::Input::<String>::new()
.with_prompt(prompt)
.allow_empty(allow_empty);
let input = if let Some(d) = default {
input.default(d)
} else {
input
};
if let Some(term) = &self.term {
Ok(input.interact_text_on(term)?)
} else {
Ok(input.interact_text()?)
}
}
fn confirm(&self, prompt: &str, default: bool) -> Result<bool> {
let confirm = dialoguer::Confirm::new()
.with_prompt(prompt)
.default(default);
if let Some(term) = &self.term {
Ok(confirm.interact_on(term)?)
} else {
Ok(confirm.interact()?)
}
}
fn password(&self, prompt: &str, confirm: Option<&str>) -> Result<String> {
let p = dialoguer::Password::new().with_prompt(prompt);
let p = if let Some(c) = confirm {
p.with_confirmation(c, "Passwords mismatching")
} else {
p
};
if let Some(term) = &self.term {
Ok(p.interact_on(term)?)
} else {
Ok(p.interact()?)
}
}
fn select(&self, prompt: &str, items: &[&str], default: usize) -> Result<usize> {
let select = dialoguer::Select::new()
.with_prompt(prompt)
.items(items)
.default(default);
if let Some(term) = &self.term {
Ok(select.interact_on(term)?)
} else {
Ok(select.interact()?)
}
}
fn print_info(&self, msg: &str) {
println!("{} {}", INFO, msg);
}
fn print_success(&self, msg: &str) {
println!("{} {}", SUCCESS, msg);
}
fn print_error(&self, msg: &str) {
println!("{} {}", ERROR, msg);
}
fn print_warn(&self, msg: &str) {
println!("{} {}", WARN, msg);
}
}
pub struct MockUi {
pub inputs: std::sync::Mutex<Vec<String>>,
pub confirms: std::sync::Mutex<Vec<bool>>,
pub selects: std::sync::Mutex<Vec<usize>>,
pub passwords: std::sync::Mutex<Vec<String>>,
}
impl Ui for MockUi {
fn input(&self, _prompt: &str, _default: Option<String>, _allow_empty: bool) -> Result<String> {
let res = {
let mut inputs = self
.inputs
.lock()
.map_err(|e| anyhow::anyhow!("Mutex poisoned: {}", e))?;
if inputs.is_empty() {
anyhow::bail!("No more mock inputs");
}
inputs.remove(0)
};
Ok(res)
}
fn confirm(&self, _prompt: &str, _default: bool) -> Result<bool> {
let res = {
let mut confirms = self
.confirms
.lock()
.map_err(|e| anyhow::anyhow!("Mutex poisoned: {}", e))?;
if confirms.is_empty() {
anyhow::bail!("No more mock confirms");
}
confirms.remove(0)
};
Ok(res)
}
fn password(&self, _prompt: &str, _confirm: Option<&str>) -> Result<String> {
let res = {
let mut p = self
.passwords
.lock()
.map_err(|e| anyhow::anyhow!("Mutex poisoned: {}", e))?;
if p.is_empty() {
return Err(anyhow::anyhow!("Mock passwords missing"));
}
p.swap_remove(0)
};
Ok(res.chars().collect())
}
fn select(&self, _prompt: &str, _items: &[&str], _default: usize) -> Result<usize> {
let res = {
let mut selects = self
.selects
.lock()
.map_err(|e| anyhow::anyhow!("Mutex poisoned: {}", e))?;
if selects.is_empty() {
anyhow::bail!("No more mock selects");
}
selects.remove(0)
};
Ok(res)
}
fn print_info(&self, _msg: &str) {}
fn print_success(&self, _msg: &str) {}
fn print_error(&self, _msg: &str) {}
fn print_warn(&self, _msg: &str) {}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dialoguer_ui_new() {
let ui = DialoguerUi::new();
assert!(ui.term.is_none());
}
#[test]
fn test_dialoguer_ui_with_term() {
let term = console::Term::stdout();
let ui = DialoguerUi::with_term(term);
assert!(ui.term.is_some());
}
#[test]
fn test_dialoguer_ui_default() {
let ui = DialoguerUi::default();
assert!(ui.term.is_none());
}
#[test]
fn test_create_progress_bar() {
let pb = create_progress_bar(100, "test progress");
assert_eq!(pb.length(), Some(100));
assert_eq!(pb.message(), "test progress");
assert_eq!(pb.position(), 0);
assert!(!pb.is_finished());
}
#[test]
fn test_create_progress_bar_zero_len() {
let pb = create_progress_bar(0, "test empty");
assert_eq!(pb.length(), Some(0));
assert_eq!(pb.message(), "test empty");
assert_eq!(pb.position(), 0);
assert!(!pb.is_finished());
}
#[test]
fn test_create_spinner() {
let pb = create_spinner("test spinner");
assert_eq!(pb.message(), "test spinner");
assert_eq!(pb.length(), None);
assert_eq!(pb.position(), 0);
assert!(!pb.is_finished());
}
}