use crate::errors::CliError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Severity {
Mild,
#[allow(dead_code)]
Severe,
}
#[derive(Debug, Clone, Copy)]
pub struct ConfirmCfg {
pub yes_count: u8,
pub no_input: bool,
pub is_tty: bool,
}
impl ConfirmCfg {
pub fn new(yes_count: u8, no_input: bool, is_tty: bool) -> Self {
Self {
yes_count,
no_input,
is_tty,
}
}
}
pub fn decide_without_prompt(severity: Severity, cfg: ConfirmCfg) -> Result<bool, CliError> {
let required = match severity {
Severity::Mild => 1,
Severity::Severe => 2,
};
if cfg.yes_count >= required {
return Ok(true);
}
if !cfg.is_tty || cfg.no_input {
return Err(CliError::NeedsConfirmation);
}
Ok(false)
}
pub fn confirm_mild(ctx: &crate::context::Ctx, message: &str) -> Result<(), CliError> {
let cfg = ConfirmCfg::new(
ctx.global.yes_count,
ctx.global.no_input,
ctx.out.stdout_is_tty,
);
let proceed = match decide_without_prompt(Severity::Mild, cfg)? {
true => true,
false => prompt_yes_no(message)?,
};
if !proceed {
return Err(CliError::Cancelled);
}
Ok(())
}
pub fn prompt_yes_no(message: &str) -> Result<bool, CliError> {
use dialoguer::Confirm;
Confirm::new()
.with_prompt(message)
.default(false)
.report(false)
.interact()
.map_err(|e| CliError::Io(std::io::Error::other(e)))
}
#[allow(dead_code)]
pub fn prompt_typed(message: &str, expected: &str) -> Result<bool, CliError> {
use dialoguer::Input;
let typed: String = Input::new()
.with_prompt(message)
.allow_empty(true)
.interact_text()
.map_err(|e| CliError::Io(std::io::Error::other(e)))?;
Ok(typed == expected)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mild_with_one_yes_is_auto() {
let cfg = ConfirmCfg::new(1, false, true);
assert!(decide_without_prompt(Severity::Mild, cfg).unwrap());
}
#[test]
fn mild_with_two_yes_is_still_auto() {
let cfg = ConfirmCfg::new(2, false, false);
assert!(decide_without_prompt(Severity::Mild, cfg).unwrap());
}
#[test]
fn mild_tty_no_yes_returns_false_so_caller_prompts() {
let cfg = ConfirmCfg::new(0, false, true);
assert!(!decide_without_prompt(Severity::Mild, cfg).unwrap());
}
#[test]
fn mild_non_tty_no_yes_needs_confirmation() {
let cfg = ConfirmCfg::new(0, false, false);
let err = decide_without_prompt(Severity::Mild, cfg).unwrap_err();
assert!(matches!(err, CliError::NeedsConfirmation));
}
#[test]
fn mild_no_input_no_yes_needs_confirmation_even_on_tty() {
let cfg = ConfirmCfg::new(0, true, true);
let err = decide_without_prompt(Severity::Mild, cfg).unwrap_err();
assert!(matches!(err, CliError::NeedsConfirmation));
}
#[test]
fn severe_needs_two_yes() {
let cfg = ConfirmCfg::new(1, false, false);
let err = decide_without_prompt(Severity::Severe, cfg).unwrap_err();
assert!(matches!(err, CliError::NeedsConfirmation));
}
#[test]
fn severe_with_two_yes_proceeds() {
let cfg = ConfirmCfg::new(2, false, false);
assert!(decide_without_prompt(Severity::Severe, cfg).unwrap());
}
#[test]
fn severe_tty_prompts() {
let cfg = ConfirmCfg::new(0, false, true);
assert!(!decide_without_prompt(Severity::Severe, cfg).unwrap());
}
}