use {
crate::{
ci::{interactive_message, is_ci},
interface::is_tui,
ui::{confirm_dialog::confirm_delete_tui, fail_message},
},
anyhow::{anyhow, Result},
console::style,
dialoguer::{console::Term, theme::ColorfulTheme, Confirm, Input, Password, Select},
};
fn ci_required(what: &str) -> anyhow::Error {
anyhow!(fail_message(&interactive_message(what)))
}
fn io_error(err: dialoguer::Error) -> anyhow::Error {
anyhow!(fail_message(&format!("Prompt failed: {err}")))
}
pub fn confirm_delete(what: &str, message: &str) -> Result<bool> {
if is_ci() {
return Err(ci_required(what));
}
if is_tui() {
return confirm_delete_tui(message).map_err(|e| anyhow!(fail_message(&e.to_string())));
}
confirm(message, false)
}
fn print_danger_warning(warning: &str) {
println!();
println!("{}", style("⚠ This action cannot be undone.").red().bold());
println!("{warning}");
println!();
}
pub fn confirm_delete_typed(what: &str, warning: &str, resource_identifier: &str) -> Result<bool> {
if is_ci() {
return Err(ci_required(what));
}
if is_tui() {
return confirm_delete_tui(warning).map_err(|e| anyhow!(fail_message(&e.to_string())));
}
print_danger_warning(warning);
let typed = Input::<String>::with_theme(&ColorfulTheme::default())
.with_prompt(format!("Type \"{resource_identifier}\" to confirm"))
.allow_empty(true)
.interact()
.map_err(io_error)?;
Ok(typed.trim() == resource_identifier)
}
pub fn confirm_delete_double(
what: &str,
warning: &str,
resource_identifier: &str,
intent_phrase: &str,
) -> Result<bool> {
if is_ci() {
return Err(ci_required(what));
}
if is_tui() {
return confirm_delete_tui(warning).map_err(|e| anyhow!(fail_message(&e.to_string())));
}
print_danger_warning(warning);
let typed_identifier = Input::<String>::with_theme(&ColorfulTheme::default())
.with_prompt(format!("Type \"{resource_identifier}\" to confirm"))
.allow_empty(true)
.interact()
.map_err(io_error)?;
if typed_identifier.trim() != resource_identifier {
return Ok(false);
}
let typed_phrase = Input::<String>::with_theme(&ColorfulTheme::default())
.with_prompt(format!("Type \"{intent_phrase}\" to confirm"))
.allow_empty(true)
.interact()
.map_err(io_error)?;
Ok(typed_phrase.trim() == intent_phrase)
}
pub fn confirm(prompt: &str, default: bool) -> Result<bool> {
if is_ci() {
return Ok(default);
}
Confirm::with_theme(&ColorfulTheme::default())
.with_prompt(prompt)
.default(default)
.interact()
.map_err(io_error)
}
pub fn input(prompt: &str, default: &str) -> Result<String> {
if is_ci() {
return Ok(default.to_string());
}
Input::<String>::with_theme(&ColorfulTheme::default())
.with_prompt(prompt)
.default(default.to_string())
.interact()
.map_err(io_error)
}
pub fn input_optional(prompt: &str, default: &str) -> Result<String> {
if is_ci() {
return Ok(default.to_string());
}
Input::<String>::with_theme(&ColorfulTheme::default())
.with_prompt(prompt)
.default(default.to_string())
.allow_empty(true)
.interact()
.map_err(io_error)
}
pub fn input_required(prompt: &str) -> Result<String> {
if is_ci() {
return Err(ci_required(prompt));
}
Input::<String>::with_theme(&ColorfulTheme::default())
.with_prompt(prompt)
.interact()
.map_err(io_error)
}
pub fn password(prompt: &str) -> Result<String> {
if is_ci() {
return Err(ci_required(prompt));
}
Password::with_theme(&ColorfulTheme::default())
.with_prompt(prompt)
.interact()
.map_err(io_error)
}
pub fn select<T: ToString>(
prompt: &str,
items: &[T],
default: usize,
ci_default: Option<usize>,
) -> Result<usize> {
if is_ci() {
return ci_default.ok_or_else(|| ci_required(prompt));
}
Select::with_theme(&ColorfulTheme::default())
.with_prompt(prompt)
.items(items)
.default(default)
.interact_on(&Term::stderr())
.map_err(io_error)
}
pub fn select_opt<T: ToString>(
prompt: &str,
items: &[T],
default: usize,
ci_default: Option<usize>,
) -> Result<Option<usize>> {
if is_ci() {
return Ok(Some(ci_default.ok_or_else(|| ci_required(prompt))?));
}
Select::with_theme(&ColorfulTheme::default())
.with_prompt(prompt)
.items(items)
.default(default)
.interact_on_opt(&Term::stderr())
.map_err(io_error)
}