aboutsummaryrefslogtreecommitdiff
path: root/vendor/dialoguer/src/prompts/input.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/dialoguer/src/prompts/input.rs')
-rw-r--r--vendor/dialoguer/src/prompts/input.rs691
1 files changed, 0 insertions, 691 deletions
diff --git a/vendor/dialoguer/src/prompts/input.rs b/vendor/dialoguer/src/prompts/input.rs
deleted file mode 100644
index b7cd829..0000000
--- a/vendor/dialoguer/src/prompts/input.rs
+++ /dev/null
@@ -1,691 +0,0 @@
-use std::{cmp::Ordering, fmt::Debug, io, iter, str::FromStr};
-
-#[cfg(feature = "completion")]
-use crate::completion::Completion;
-#[cfg(feature = "history")]
-use crate::history::History;
-use crate::{
- theme::{SimpleTheme, TermThemeRenderer, Theme},
- validate::Validator,
-};
-
-use console::{Key, Term};
-
-type ValidatorCallback<'a, T> = Box<dyn FnMut(&T) -> Option<String> + 'a>;
-
-/// Renders an input prompt.
-///
-/// ## Example usage
-///
-/// ```rust,no_run
-/// use dialoguer::Input;
-///
-/// # fn test() -> Result<(), Box<dyn std::error::Error>> {
-/// let input : String = Input::new()
-/// .with_prompt("Tea or coffee?")
-/// .with_initial_text("Yes")
-/// .default("No".into())
-/// .interact_text()?;
-/// # Ok(())
-/// # }
-/// ```
-/// It can also be used with turbofish notation:
-///
-/// ```rust,no_run
-/// # fn test() -> Result<(), Box<dyn std::error::Error>> {
-/// # use dialoguer::Input;
-/// let input = Input::<String>::new()
-/// .interact_text()?;
-/// # Ok(())
-/// # }
-/// ```
-pub struct Input<'a, T> {
- prompt: String,
- post_completion_text: Option<String>,
- report: bool,
- default: Option<T>,
- show_default: bool,
- initial_text: Option<String>,
- theme: &'a dyn Theme,
- permit_empty: bool,
- validator: Option<ValidatorCallback<'a, T>>,
- #[cfg(feature = "history")]
- history: Option<&'a mut dyn History<T>>,
- #[cfg(feature = "completion")]
- completion: Option<&'a dyn Completion>,
-}
-
-impl<T> Default for Input<'static, T> {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl<T> Input<'_, T> {
- /// Creates an input prompt.
- pub fn new() -> Self {
- Self::with_theme(&SimpleTheme)
- }
-
- /// Sets the input prompt.
- pub fn with_prompt<S: Into<String>>(&mut self, prompt: S) -> &mut Self {
- self.prompt = prompt.into();
- self
- }
-
- /// Changes the prompt text to the post completion text after input is complete
- pub fn with_post_completion_text<S: Into<String>>(
- &mut self,
- post_completion_text: S,
- ) -> &mut Self {
- self.post_completion_text = Some(post_completion_text.into());
- self
- }
-
- /// Indicates whether to report the input value after interaction.
- ///
- /// The default is to report the input value.
- pub fn report(&mut self, val: bool) -> &mut Self {
- self.report = val;
- self
- }
-
- /// Sets initial text that user can accept or erase.
- pub fn with_initial_text<S: Into<String>>(&mut self, val: S) -> &mut Self {
- self.initial_text = Some(val.into());
- 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, value: T) -> &mut Self {
- self.default = Some(value);
- self
- }
-
- /// Enables or disables an empty input
- ///
- /// By default, if there is no default value set for the input, the user must input a non-empty string.
- pub fn allow_empty(&mut self, val: bool) -> &mut Self {
- self.permit_empty = val;
- self
- }
-
- /// Disables or enables the default value display.
- ///
- /// The default behaviour is to append [`default`](#method.default) to the prompt to tell the
- /// user what is the default value.
- ///
- /// This method does not affect existence of default value, only its display in the prompt!
- pub fn show_default(&mut self, val: bool) -> &mut Self {
- self.show_default = val;
- self
- }
-}
-
-impl<'a, T> Input<'a, T> {
- /// Creates an input prompt with a specific theme.
- pub fn with_theme(theme: &'a dyn Theme) -> Self {
- Self {
- prompt: "".into(),
- post_completion_text: None,
- report: true,
- default: None,
- show_default: true,
- initial_text: None,
- theme,
- permit_empty: false,
- validator: None,
- #[cfg(feature = "history")]
- history: None,
- #[cfg(feature = "completion")]
- completion: None,
- }
- }
-
- /// Enable history processing
- ///
- /// # Example
- ///
- /// ```no_run
- /// # use dialoguer::{History, Input};
- /// # use std::{collections::VecDeque, fmt::Display};
- /// let mut history = MyHistory::default();
- /// loop {
- /// if let Ok(input) = Input::<String>::new()
- /// .with_prompt("hist")
- /// .history_with(&mut history)
- /// .interact_text()
- /// {
- /// // Do something with the input
- /// }
- /// }
- /// # struct MyHistory {
- /// # history: VecDeque<String>,
- /// # }
- /// #
- /// # impl Default for MyHistory {
- /// # fn default() -> Self {
- /// # MyHistory {
- /// # history: VecDeque::new(),
- /// # }
- /// # }
- /// # }
- /// #
- /// # impl<T: ToString> History<T> for MyHistory {
- /// # fn read(&self, pos: usize) -> Option<String> {
- /// # self.history.get(pos).cloned()
- /// # }
- /// #
- /// # fn write(&mut self, val: &T)
- /// # where
- /// # {
- /// # self.history.push_front(val.to_string());
- /// # }
- /// # }
- /// ```
- #[cfg(feature = "history")]
- pub fn history_with<H>(&mut self, history: &'a mut H) -> &mut Self
- where
- H: History<T>,
- {
- self.history = Some(history);
- self
- }
-
- /// Enable completion
- #[cfg(feature = "completion")]
- pub fn completion_with<C>(&mut self, completion: &'a C) -> &mut Self
- where
- C: Completion,
- {
- self.completion = Some(completion);
- self
- }
-}
-
-impl<'a, T> Input<'a, T>
-where
- T: 'a,
-{
- /// Registers a validator.
- ///
- /// # Example
- ///
- /// ```no_run
- /// # use dialoguer::Input;
- /// let mail: String = Input::new()
- /// .with_prompt("Enter email")
- /// .validate_with(|input: &String| -> Result<(), &str> {
- /// if input.contains('@') {
- /// Ok(())
- /// } else {
- /// Err("This is not a mail address")
- /// }
- /// })
- /// .interact()
- /// .unwrap();
- /// ```
- pub fn validate_with<V>(&mut self, mut validator: V) -> &mut Self
- where
- V: Validator<T> + 'a,
- V::Err: ToString,
- {
- let mut old_validator_func = self.validator.take();
-
- self.validator = Some(Box::new(move |value: &T| -> Option<String> {
- if let Some(old) = old_validator_func.as_mut() {
- if let Some(err) = old(value) {
- return Some(err);
- }
- }
-
- match validator.validate(value) {
- Ok(()) => None,
- Err(err) => Some(err.to_string()),
- }
- }));
-
- self
- }
-}
-
-impl<T> Input<'_, T>
-where
- T: Clone + ToString + FromStr,
- <T as FromStr>::Err: Debug + ToString,
-{
- /// Enables the user to enter a printable ascii sequence and returns the result.
- ///
- /// Its difference from [`interact`](#method.interact) is that it only allows ascii characters for string,
- /// while [`interact`](#method.interact) allows virtually any character to be used e.g arrow keys.
- ///
- /// The dialog is rendered on stderr.
- pub fn interact_text(&mut self) -> io::Result<T> {
- self.interact_text_on(&Term::stderr())
- }
-
- /// Like [`interact_text`](#method.interact_text) but allows a specific terminal to be set.
- pub fn interact_text_on(&mut self, term: &Term) -> io::Result<T> {
- let mut render = TermThemeRenderer::new(term, self.theme);
-
- loop {
- let default_string = self.default.as_ref().map(ToString::to_string);
-
- let prompt_len = render.input_prompt(
- &self.prompt,
- if self.show_default {
- default_string.as_deref()
- } else {
- None
- },
- )?;
-
- // Read input by keystroke so that we can suppress ascii control characters
- if !term.features().is_attended() {
- return Ok("".to_owned().parse::<T>().unwrap());
- }
-
- let mut chars: Vec<char> = Vec::new();
- let mut position = 0;
- #[cfg(feature = "history")]
- let mut hist_pos = 0;
-
- if let Some(initial) = self.initial_text.as_ref() {
- term.write_str(initial)?;
- chars = initial.chars().collect();
- position = chars.len();
- }
- term.flush()?;
-
- loop {
- match term.read_key()? {
- Key::Backspace if position > 0 => {
- position -= 1;
- chars.remove(position);
- let line_size = term.size().1 as usize;
- // Case we want to delete last char of a line so the cursor is at the beginning of the next line
- if (position + prompt_len) % (line_size - 1) == 0 {
- term.clear_line()?;
- term.move_cursor_up(1)?;
- term.move_cursor_right(line_size + 1)?;
- } else {
- term.clear_chars(1)?;
- }
-
- let tail: String = chars[position..].iter().collect();
-
- if !tail.is_empty() {
- term.write_str(&tail)?;
-
- let total = position + prompt_len + tail.len();
- let total_line = total / line_size;
- let line_cursor = (position + prompt_len) / line_size;
- term.move_cursor_up(total_line - line_cursor)?;
-
- term.move_cursor_left(line_size)?;
- term.move_cursor_right((position + prompt_len) % line_size)?;
- }
-
- term.flush()?;
- }
- Key::Char(chr) if !chr.is_ascii_control() => {
- chars.insert(position, chr);
- position += 1;
- let tail: String =
- iter::once(&chr).chain(chars[position..].iter()).collect();
- term.write_str(&tail)?;
- term.move_cursor_left(tail.len() - 1)?;
- term.flush()?;
- }
- Key::ArrowLeft if position > 0 => {
- if (position + prompt_len) % term.size().1 as usize == 0 {
- term.move_cursor_up(1)?;
- term.move_cursor_right(term.size().1 as usize)?;
- } else {
- term.move_cursor_left(1)?;
- }
- position -= 1;
- term.flush()?;
- }
- Key::ArrowRight if position < chars.len() => {
- if (position + prompt_len) % (term.size().1 as usize - 1) == 0 {
- term.move_cursor_down(1)?;
- term.move_cursor_left(term.size().1 as usize)?;
- } else {
- term.move_cursor_right(1)?;
- }
- position += 1;
- term.flush()?;
- }
- Key::UnknownEscSeq(seq) if seq == vec!['b'] => {
- let line_size = term.size().1 as usize;
- let nb_space = chars[..position]
- .iter()
- .rev()
- .take_while(|c| c.is_whitespace())
- .count();
- let find_last_space = chars[..position - nb_space]
- .iter()
- .rposition(|c| c.is_whitespace());
-
- // If we find a space we set the cursor to the next char else we set it to the beginning of the input
- if let Some(mut last_space) = find_last_space {
- if last_space < position {
- last_space += 1;
- let new_line = (prompt_len + last_space) / line_size;
- let old_line = (prompt_len + position) / line_size;
- let diff_line = old_line - new_line;
- if diff_line != 0 {
- term.move_cursor_up(old_line - new_line)?;
- }
-
- let new_pos_x = (prompt_len + last_space) % line_size;
- let old_pos_x = (prompt_len + position) % line_size;
- let diff_pos_x = new_pos_x as i64 - old_pos_x as i64;
- //println!("new_pos_x = {}, old_pos_x = {}, diff = {}", new_pos_x, old_pos_x, diff_pos_x);
- if diff_pos_x < 0 {
- term.move_cursor_left(-diff_pos_x as usize)?;
- } else {
- term.move_cursor_right((diff_pos_x) as usize)?;
- }
- position = last_space;
- }
- } else {
- term.move_cursor_left(position)?;
- position = 0;
- }
-
- term.flush()?;
- }
- Key::UnknownEscSeq(seq) if seq == vec!['f'] => {
- let line_size = term.size().1 as usize;
- let find_next_space =
- chars[position..].iter().position(|c| c.is_whitespace());
-
- // If we find a space we set the cursor to the next char else we set it to the beginning of the input
- if let Some(mut next_space) = find_next_space {
- let nb_space = chars[position + next_space..]
- .iter()
- .take_while(|c| c.is_whitespace())
- .count();
- next_space += nb_space;
- let new_line = (prompt_len + position + next_space) / line_size;
- let old_line = (prompt_len + position) / line_size;
- term.move_cursor_down(new_line - old_line)?;
-
- let new_pos_x = (prompt_len + position + next_space) % line_size;
- let old_pos_x = (prompt_len + position) % line_size;
- let diff_pos_x = new_pos_x as i64 - old_pos_x as i64;
- if diff_pos_x < 0 {
- term.move_cursor_left(-diff_pos_x as usize)?;
- } else {
- term.move_cursor_right((diff_pos_x) as usize)?;
- }
- position += next_space;
- } else {
- let new_line = (prompt_len + chars.len()) / line_size;
- let old_line = (prompt_len + position) / line_size;
- term.move_cursor_down(new_line - old_line)?;
-
- let new_pos_x = (prompt_len + chars.len()) % line_size;
- let old_pos_x = (prompt_len + position) % line_size;
- let diff_pos_x = new_pos_x as i64 - old_pos_x as i64;
- match diff_pos_x.cmp(&0) {
- Ordering::Less => {
- term.move_cursor_left((-diff_pos_x - 1) as usize)?;
- }
- Ordering::Equal => {}
- Ordering::Greater => {
- term.move_cursor_right((diff_pos_x) as usize)?;
- }
- }
- position = chars.len();
- }
-
- term.flush()?;
- }
- #[cfg(feature = "completion")]
- Key::ArrowRight | Key::Tab => {
- if let Some(completion) = &self.completion {
- let input: String = chars.clone().into_iter().collect();
- if let Some(x) = completion.get(&input) {
- term.clear_chars(chars.len())?;
- chars.clear();
- position = 0;
- for ch in x.chars() {
- chars.insert(position, ch);
- position += 1;
- }
- term.write_str(&x)?;
- term.flush()?;
- }
- }
- }
- #[cfg(feature = "history")]
- Key::ArrowUp => {
- let line_size = term.size().1 as usize;
- if let Some(history) = &self.history {
- if let Some(previous) = history.read(hist_pos) {
- hist_pos += 1;
- let mut chars_len = chars.len();
- while ((prompt_len + chars_len) / line_size) > 0 {
- term.clear_chars(chars_len)?;
- if (prompt_len + chars_len) % line_size == 0 {
- chars_len -= std::cmp::min(chars_len, line_size);
- } else {
- chars_len -= std::cmp::min(
- chars_len,
- (prompt_len + chars_len + 1) % line_size,
- );
- }
- if chars_len > 0 {
- term.move_cursor_up(1)?;
- term.move_cursor_right(line_size)?;
- }
- }
- term.clear_chars(chars_len)?;
- chars.clear();
- position = 0;
- for ch in previous.chars() {
- chars.insert(position, ch);
- position += 1;
- }
- term.write_str(&previous)?;
- term.flush()?;
- }
- }
- }
- #[cfg(feature = "history")]
- Key::ArrowDown => {
- let line_size = term.size().1 as usize;
- if let Some(history) = &self.history {
- let mut chars_len = chars.len();
- while ((prompt_len + chars_len) / line_size) > 0 {
- term.clear_chars(chars_len)?;
- if (prompt_len + chars_len) % line_size == 0 {
- chars_len -= std::cmp::min(chars_len, line_size);
- } else {
- chars_len -= std::cmp::min(
- chars_len,
- (prompt_len + chars_len + 1) % line_size,
- );
- }
- if chars_len > 0 {
- term.move_cursor_up(1)?;
- term.move_cursor_right(line_size)?;
- }
- }
- term.clear_chars(chars_len)?;
- chars.clear();
- position = 0;
- // Move the history position back one in case we have up arrowed into it
- // and the position is sitting on the next to read
- if let Some(pos) = hist_pos.checked_sub(1) {
- hist_pos = pos;
- // Move it back again to get the previous history entry
- if let Some(pos) = pos.checked_sub(1) {
- if let Some(previous) = history.read(pos) {
- for ch in previous.chars() {
- chars.insert(position, ch);
- position += 1;
- }
- term.write_str(&previous)?;
- }
- }
- }
- term.flush()?;
- }
- }
- Key::Enter => break,
- _ => (),
- }
- }
- let input = chars.iter().collect::<String>();
-
- term.clear_line()?;
- render.clear()?;
-
- if chars.is_empty() {
- if let Some(ref default) = self.default {
- if let Some(ref mut validator) = self.validator {
- if let Some(err) = validator(default) {
- render.error(&err)?;
- continue;
- }
- }
-
- if self.report {
- render.input_prompt_selection(&self.prompt, &default.to_string())?;
- }
- term.flush()?;
- return Ok(default.clone());
- } else if !self.permit_empty {
- continue;
- }
- }
-
- match input.parse::<T>() {
- Ok(value) => {
- if let Some(ref mut validator) = self.validator {
- if let Some(err) = validator(&value) {
- render.error(&err)?;
- continue;
- }
- }
-
- #[cfg(feature = "history")]
- if let Some(history) = &mut self.history {
- history.write(&value);
- }
-
- if self.report {
- if let Some(post_completion_text) = &self.post_completion_text {
- render.input_prompt_selection(post_completion_text, &input)?;
- } else {
- render.input_prompt_selection(&self.prompt, &input)?;
- }
- }
- term.flush()?;
-
- return Ok(value);
- }
- Err(err) => {
- render.error(&err.to_string())?;
- continue;
- }
- }
- }
- }
-}
-
-impl<T> Input<'_, T>
-where
- T: Clone + ToString + FromStr,
- <T as 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`](#method.interact_text).
- ///
- /// If the user confirms the result is `true`, `false` otherwise.
- /// The dialog is rendered on stderr.
- pub fn interact(&mut self) -> io::Result<T> {
- self.interact_on(&Term::stderr())
- }
-
- /// Like [`interact`](#method.interact) but allows a specific terminal to be set.
- pub fn interact_on(&mut self, term: &Term) -> io::Result<T> {
- let mut render = TermThemeRenderer::new(term, self.theme);
-
- loop {
- let default_string = self.default.as_ref().map(ToString::to_string);
-
- render.input_prompt(
- &self.prompt,
- if self.show_default {
- default_string.as_deref()
- } else {
- None
- },
- )?;
- term.flush()?;
-
- let input = if let Some(initial_text) = self.initial_text.as_ref() {
- term.read_line_initial_text(initial_text)?
- } else {
- term.read_line()?
- };
-
- render.add_line();
- term.clear_line()?;
- render.clear()?;
-
- if input.is_empty() {
- if let Some(ref default) = self.default {
- if let Some(ref mut validator) = self.validator {
- if let Some(err) = validator(default) {
- render.error(&err)?;
- continue;
- }
- }
-
- if self.report {
- render.input_prompt_selection(&self.prompt, &default.to_string())?;
- }
- term.flush()?;
- return Ok(default.clone());
- } else if !self.permit_empty {
- continue;
- }
- }
-
- match input.parse::<T>() {
- Ok(value) => {
- if let Some(ref mut validator) = self.validator {
- if let Some(err) = validator(&value) {
- render.error(&err)?;
- continue;
- }
- }
-
- if self.report {
- render.input_prompt_selection(&self.prompt, &input)?;
- }
- term.flush()?;
-
- return Ok(value);
- }
- Err(err) => {
- render.error(&err.to_string())?;
- continue;
- }
- }
- }
- }
-}