diff --git a/src/prompts/input.rs b/src/prompts/input.rs index 510aecf..223cf65 100644 --- a/src/prompts/input.rs +++ b/src/prompts/input.rs @@ -1,6 +1,5 @@ use std::{ cmp::Ordering, - fmt::Debug, io, iter, str::FromStr, sync::{Arc, Mutex}, @@ -281,7 +280,7 @@ where impl Input<'_, T> where T: Clone + ToString + FromStr, - ::Err: Debug + ToString, + ::Err: ToString, { /// Enables the user to enter a printable ascii sequence and returns the result. /// @@ -289,12 +288,39 @@ where /// while [`interact`](Self::interact) allows virtually any character to be used e.g arrow keys. /// /// The dialog is rendered on stderr. + /// This unlike [`interact_text_opt`](Self::interact_text_opt) does not allow to quit with 'Esc'. + #[inline] pub fn interact_text(self) -> Result { self.interact_text_on(&Term::stderr()) } + /// Enables the user to enter a printable ascii sequence and returns the result. + /// + /// Unlike [`interact`](Self::interact) it only allows ascii characters for string, + /// and can be cancelled with 'Esc'. + /// + /// The dialog is rendered on stderr. + /// Result contains `Some(T)` if user hit 'Enter' or `None` if user cancelled with 'Esc'. + #[inline] + pub fn interact_text_opt(self) -> Result> { + self.interact_text_on_opt(&Term::stderr()) + } + /// Like [`interact_text`](Self::interact_text) but allows a specific terminal to be set. - pub fn interact_text_on(mut self, term: &Term) -> Result { + #[inline] + pub fn interact_text_on(self, term: &Term) -> Result { + Ok(self + ._interact_text_on(term, false)? + .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Quit not allowed in this case"))?) + } + + /// Like [`interact_text_opt`](Self::interact_text_opt) but allows a specific terminal to be set. + #[inline] + pub fn interact_text_on_opt(self, term: &Term) -> Result> { + self._interact_text_on(term, true) + } + + fn _interact_text_on(mut self, term: &Term, allow_quit: bool) -> Result> { if !term.is_term() { return Err(io::Error::new(io::ErrorKind::NotConnected, "not a terminal").into()); } @@ -313,11 +339,6 @@ where }, )?; - // Read input by keystroke so that we can suppress ascii control characters - if !term.features().is_attended() { - return Ok("".to_owned().parse::().unwrap()); - } - let mut chars: Vec = Vec::new(); let mut position = 0; #[cfg(feature = "history")] @@ -332,6 +353,13 @@ where loop { match term.read_key()? { + Key::Escape if allow_quit => { + term.clear_line()?; + render.clear()?; + term.flush()?; + term.show_cursor()?; + return Ok(None); + } Key::Backspace if position > 0 => { position -= 1; chars.remove(position); @@ -591,7 +619,7 @@ where render.input_prompt_selection(&self.prompt, &default.to_string())?; } term.flush()?; - return Ok(default.clone()); + return Ok(Some(default.clone())); } else if !self.permit_empty { continue; } @@ -620,7 +648,7 @@ where } term.flush()?; - return Ok(value); + return Ok(Some(value)); } Err(err) => { render.error(&err.to_string())?; @@ -629,20 +657,14 @@ where } } } -} -impl Input<'_, T> -where - T: Clone + ToString + FromStr, - ::Err: ToString, -{ /// Enables user interaction and returns the result. /// /// Allows any characters as input, including e.g arrow keys. /// Some of the keys might have undesired behavior. /// For more limited version, see [`interact_text`](Self::interact_text). /// - /// If the user confirms the result is `true`, `false` otherwise. + /// Returns the parsed value once the user validates their input with 'Enter'. /// The dialog is rendered on stderr. pub fn interact(self) -> Result { self.interact_on(&Term::stderr())