diff options
Diffstat (limited to 'vendor/dialoguer/src/prompts/confirm.rs')
-rw-r--r-- | vendor/dialoguer/src/prompts/confirm.rs | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/vendor/dialoguer/src/prompts/confirm.rs b/vendor/dialoguer/src/prompts/confirm.rs new file mode 100644 index 0000000..24bcc4c --- /dev/null +++ b/vendor/dialoguer/src/prompts/confirm.rs @@ -0,0 +1,287 @@ +use std::io; + +use crate::theme::{SimpleTheme, TermThemeRenderer, Theme}; + +use console::{Key, Term}; + +/// Renders a confirm prompt. +/// +/// ## Example usage +/// +/// ```rust,no_run +/// # fn test() -> Result<(), Box<dyn std::error::Error>> { +/// use dialoguer::Confirm; +/// +/// if Confirm::new().with_prompt("Do you want to continue?").interact()? { +/// println!("Looks like you want to continue"); +/// } else { +/// println!("nevermind then :("); +/// } +/// # Ok(()) } fn main() { test().unwrap(); } +/// ``` +pub struct Confirm<'a> { + prompt: String, + report: bool, + default: Option<bool>, + show_default: bool, + wait_for_newline: bool, + theme: &'a dyn Theme, +} + +impl Default for Confirm<'static> { + fn default() -> Self { + Self::new() + } +} + +impl Confirm<'static> { + /// Creates a confirm prompt. + pub fn new() -> Self { + Self::with_theme(&SimpleTheme) + } +} + +impl Confirm<'_> { + /// Sets the confirm prompt. + pub fn with_prompt<S: Into<String>>(&mut self, prompt: S) -> &mut Self { + self.prompt = prompt.into(); + self + } + + /// Indicates whether or not to report the chosen selection after interaction. + /// + /// The default is to report the chosen selection. + pub fn report(&mut self, val: bool) -> &mut Self { + self.report = val; + self + } + + #[deprecated(note = "Use with_prompt() instead", since = "0.6.0")] + #[inline] + pub fn with_text(&mut self, text: &str) -> &mut Self { + self.with_prompt(text) + } + + /// Sets when to react to user input. + /// + /// When `false` (default), we check on each user keystroke immediately as + /// it is typed. Valid inputs can be one of 'y', 'n', or a newline to accept + /// the default. + /// + /// When `true`, the user must type their choice and hit the Enter key before + /// proceeding. Valid inputs can be "yes", "no", "y", "n", or an empty string + /// to accept the default. + pub fn wait_for_newline(&mut self, wait: bool) -> &mut Self { + self.wait_for_newline = wait; + self + } + + /// Sets a default. + /// + /// Out of the box the prompt does not have a default and will continue + /// to display until the user inputs something and hits enter. If a default is set the user + /// can instead accept the default with enter. + pub fn default(&mut self, val: bool) -> &mut Self { + self.default = Some(val); + self + } + + /// Disables or enables the default value display. + /// + /// The default is to append the default value to the prompt to tell the user. + pub fn show_default(&mut self, val: bool) -> &mut Self { + self.show_default = val; + self + } + + /// Enables user interaction and returns the result. + /// + /// The dialog is rendered on stderr. + /// + /// Result contains `bool` if user answered "yes" or "no" or `default` (configured in [`default`](Self::default) if pushes enter. + /// This unlike [`interact_opt`](Self::interact_opt) does not allow to quit with 'Esc' or 'q'. + #[inline] + pub fn interact(&self) -> io::Result<bool> { + self.interact_on(&Term::stderr()) + } + + /// Enables user interaction and returns the result. + /// + /// The dialog is rendered on stderr. + /// + /// Result contains `Some(bool)` if user answered "yes" or "no" or `Some(default)` (configured in [`default`](Self::default)) if pushes enter, + /// or `None` if user cancelled with 'Esc' or 'q'. + #[inline] + pub fn interact_opt(&self) -> io::Result<Option<bool>> { + self.interact_on_opt(&Term::stderr()) + } + + /// Like [interact](#method.interact) but allows a specific terminal to be set. + /// + /// ## Examples + /// + /// ```rust,no_run + /// use dialoguer::Confirm; + /// use console::Term; + /// + /// # fn main() -> std::io::Result<()> { + /// let proceed = Confirm::new() + /// .with_prompt("Do you wish to continue?") + /// .interact_on(&Term::stderr())?; + /// # Ok(()) + /// # } + /// ``` + #[inline] + pub fn interact_on(&self, term: &Term) -> io::Result<bool> { + self._interact_on(term, false)? + .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Quit not allowed in this case")) + } + + /// Like [`interact_opt`](Self::interact_opt) but allows a specific terminal to be set. + /// + /// ## Examples + /// ```rust,no_run + /// use dialoguer::Confirm; + /// use console::Term; + /// + /// fn main() -> std::io::Result<()> { + /// let confirmation = Confirm::new() + /// .interact_on_opt(&Term::stdout())?; + /// + /// match confirmation { + /// Some(answer) => println!("User answered {}", if answer { "yes" } else { "no " }), + /// None => println!("User did not answer") + /// } + /// + /// Ok(()) + /// } + /// ``` + #[inline] + pub fn interact_on_opt(&self, term: &Term) -> io::Result<Option<bool>> { + self._interact_on(term, true) + } + + fn _interact_on(&self, term: &Term, allow_quit: bool) -> io::Result<Option<bool>> { + let mut render = TermThemeRenderer::new(term, self.theme); + + let default_if_show = if self.show_default { + self.default + } else { + None + }; + + render.confirm_prompt(&self.prompt, default_if_show)?; + + term.hide_cursor()?; + term.flush()?; + + let rv; + + if self.wait_for_newline { + // Waits for user input and for the user to hit the Enter key + // before validation. + let mut value = default_if_show; + + loop { + let input = term.read_key()?; + + match input { + Key::Char('y') | Key::Char('Y') => { + value = Some(true); + } + Key::Char('n') | Key::Char('N') => { + value = Some(false); + } + Key::Enter => { + if !allow_quit { + value = value.or(self.default); + } + + if value.is_some() || allow_quit { + rv = value; + break; + } + continue; + } + Key::Escape | Key::Char('q') if allow_quit => { + value = None; + } + Key::Unknown => { + return Err(io::Error::new( + io::ErrorKind::NotConnected, + "Not a terminal", + )) + } + _ => { + continue; + } + }; + + term.clear_line()?; + render.confirm_prompt(&self.prompt, value)?; + } + } else { + // Default behavior: matches continuously on every keystroke, + // and does not wait for user to hit the Enter key. + loop { + let input = term.read_key()?; + let value = match input { + Key::Char('y') | Key::Char('Y') => Some(true), + Key::Char('n') | Key::Char('N') => Some(false), + Key::Enter if self.default.is_some() => Some(self.default.unwrap()), + Key::Escape | Key::Char('q') if allow_quit => None, + Key::Unknown => { + return Err(io::Error::new( + io::ErrorKind::NotConnected, + "Not a terminal", + )) + } + _ => { + continue; + } + }; + + rv = value; + break; + } + } + + term.clear_line()?; + if self.report { + render.confirm_prompt_selection(&self.prompt, rv)?; + } + term.show_cursor()?; + term.flush()?; + + Ok(rv) + } +} + +impl<'a> Confirm<'a> { + /// Creates a confirm prompt with a specific theme. + /// + /// ## Examples + /// ```rust,no_run + /// use dialoguer::{ + /// Confirm, + /// theme::ColorfulTheme + /// }; + /// + /// # fn main() -> std::io::Result<()> { + /// let proceed = Confirm::with_theme(&ColorfulTheme::default()) + /// .with_prompt("Do you wish to continue?") + /// .interact()?; + /// # Ok(()) + /// # } + /// ``` + pub fn with_theme(theme: &'a dyn Theme) -> Self { + Self { + prompt: "".into(), + report: true, + default: None, + show_default: true, + wait_for_newline: false, + theme, + } + } +} |