use crossterm::{
event::{self, Event, KeyCode, KeyModifiers},
terminal,
};
use glyphs::{style, Color};
use std::io::{stdout, Write};
pub fn confirm(prompt: impl Into<String>) -> Confirm {
Confirm::new(prompt)
}
pub struct Confirm {
prompt: String,
default: Option<bool>,
affirmative: String,
negative: String,
}
impl Confirm {
pub fn new(prompt: impl Into<String>) -> Self {
Self {
prompt: prompt.into(),
default: None,
affirmative: "Yes".to_string(),
negative: "No".to_string(),
}
}
#[must_use]
pub fn default(mut self, default: bool) -> Self {
self.default = Some(default);
self
}
#[must_use]
pub fn affirmative(mut self, text: impl Into<String>) -> Self {
self.affirmative = text.into();
self
}
#[must_use]
pub fn negative(mut self, text: impl Into<String>) -> Self {
self.negative = text.into();
self
}
pub fn run(self) -> bool {
let mut stdout = stdout();
terminal::enable_raw_mode().expect("Failed to enable raw mode");
let hint = match self.default {
Some(true) => format!("[{}/n]", self.affirmative.chars().next().unwrap_or('Y')),
Some(false) => format!("[y/{}]", self.negative.chars().next().unwrap_or('N')),
None => "[y/n]".to_string(),
};
print!(
"{} {} ",
style(&self.prompt).fg(Color::Cyan),
style(&hint).dim()
);
stdout.flush().ok();
let result = loop {
if let Ok(Event::Key(key)) = event::read() {
match key.code {
KeyCode::Char('y') | KeyCode::Char('Y') => break true,
KeyCode::Char('n') | KeyCode::Char('N') => break false,
KeyCode::Enter => {
if let Some(default) = self.default {
break default;
}
}
KeyCode::Esc => break false,
KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
break false
}
_ => {}
}
}
};
terminal::disable_raw_mode().expect("Failed to disable raw mode");
let answer = if result {
style(&self.affirmative).fg(Color::Green)
} else {
style(&self.negative).fg(Color::Red)
};
println!("{}", answer);
result
}
}