Skip to content

Commit

Permalink
Implement Clone for all prompts, fixes #262
Browse files Browse the repository at this point in the history
  • Loading branch information
pksunkara committed Sep 5, 2023
1 parent 5088e87 commit 9baa851
Show file tree
Hide file tree
Showing 11 changed files with 173 additions and 72 deletions.
16 changes: 13 additions & 3 deletions CHANGELOG.md
Expand Up @@ -5,8 +5,17 @@
### Enhancements

* Added `dialouger::Result` and `dialouger::Error`
* Resolve some issues on Windows where pressing shift keys sometimes aborted dialogs.
* Resolve `MultiSelect` checked and unchecked variants looking the same on Windows.
* Added a `BasicHistory` implementation for `History`
* Added vim mode for `FuzzySelect`
* All prompts implement `Clone`

### Bug fixes

* Resolve some issues on Windows where pressing shift keys sometimes aborted dialogs
* Resolve `MultiSelect` checked and unchecked variants looking the same on Windows
* `Input` values that are invalid are now also stored in `History`
* Resolve some issues with cursor positioning in `Input` when using `utf-8` characters
* Correct page is shown when default selected option is not on the first page for `Select`

### Breaking

Expand All @@ -16,6 +25,7 @@
* Prompt builder functions now return `Self` instead of `&mut Self`
* Prompt interaction functions now take `self` instead of `&self`
* Prompt interaction functions and other operations now return `dialouger::Result` instead of `std::io::Result`
* Rename `Validator` to `InputValidator`

## 0.10.4

Expand Down Expand Up @@ -111,7 +121,7 @@

## 0.6.1

### Bugfixes
### Bug fixes

* `theme::ColorfulTheme` default styles are for stderr

Expand Down
2 changes: 1 addition & 1 deletion src/history.rs
Expand Up @@ -6,7 +6,7 @@ pub trait History<T> {
/// be read from history. The `pos` represents the number
/// of times the `Up`/`Down` arrow key has been pressed.
/// This would normally be used as an index to some sort
/// of vector. If the `pos` does not have an entry, [`None`](Option::None)
/// of vector. If the `pos` does not have an entry, [`None`](Option::None)
/// should be returned.
fn read(&self, pos: usize) -> Option<String>;

Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Expand Up @@ -41,7 +41,7 @@ pub use error::{Error, Result};
#[cfg(feature = "history")]
pub use history::{BasicHistory, History};
use paging::Paging;
pub use validate::Validator;
pub use validate::{InputValidator, PasswordValidator};

#[cfg(feature = "fuzzy-select")]
pub use prompts::fuzzy_select::FuzzySelect;
Expand Down
13 changes: 13 additions & 0 deletions src/prompts/confirm.rs
Expand Up @@ -27,6 +27,7 @@ use crate::{
/// }
/// }
/// ```
#[derive(Clone)]
pub struct Confirm<'a> {
prompt: String,
report: bool,
Expand Down Expand Up @@ -262,3 +263,15 @@ impl<'a> Confirm<'a> {
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_clone() {
let confirm = Confirm::new().with_prompt("Do you want to continue?");

let _ = confirm.clone();
}
}
14 changes: 13 additions & 1 deletion src/prompts/fuzzy_select.rs
Expand Up @@ -30,7 +30,7 @@ use crate::{
/// println!("You chose: {}", items[selection]);
/// }
/// ```

#[derive(Clone)]
pub struct FuzzySelect<'a> {
default: Option<usize>,
items: Vec<String>,
Expand Down Expand Up @@ -382,3 +382,15 @@ impl<'a> FuzzySelect<'a> {
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_clone() {
let fuzzy_select = FuzzySelect::new().with_prompt("Do you want to continue?");

let _ = fuzzy_select.clone();
}
}
53 changes: 36 additions & 17 deletions src/prompts/input.rs
@@ -1,4 +1,10 @@
use std::{cmp::Ordering, fmt::Debug, io, iter, str::FromStr};
use std::{
cmp::Ordering,
fmt::Debug,
io, iter,
str::FromStr,
sync::{Arc, Mutex},
};

use console::{Key, Term};

Expand All @@ -8,11 +14,11 @@ use crate::completion::Completion;
use crate::history::History;
use crate::{
theme::{render::TermThemeRenderer, SimpleTheme, Theme},
validate::Validator,
validate::InputValidator,
Result,
};

type ValidatorCallback<'a, T> = Box<dyn FnMut(&T) -> Option<String> + 'a>;
type InputValidatorCallback<'a, T> = Arc<Mutex<dyn FnMut(&T) -> Option<String> + 'a>>;

/// Renders an input prompt.
///
Expand Down Expand Up @@ -45,6 +51,7 @@ type ValidatorCallback<'a, T> = Box<dyn FnMut(&T) -> Option<String> + 'a>;
/// println!("Your name is: {}", name);
/// }
/// ```
#[derive(Clone)]
pub struct Input<'a, T> {
prompt: String,
post_completion_text: Option<String>,
Expand All @@ -54,9 +61,9 @@ pub struct Input<'a, T> {
initial_text: Option<String>,
theme: &'a dyn Theme,
permit_empty: bool,
validator: Option<ValidatorCallback<'a, T>>,
validator: Option<InputValidatorCallback<'a, T>>,
#[cfg(feature = "history")]
history: Option<&'a mut dyn History<T>>,
history: Option<Arc<Mutex<&'a mut dyn History<T>>>>,
#[cfg(feature = "completion")]
completion: Option<&'a dyn Completion>,
}
Expand Down Expand Up @@ -207,7 +214,7 @@ impl<'a, T> Input<'a, T> {
where
H: History<T>,
{
self.history = Some(history);
self.history = Some(Arc::new(Mutex::new(history)));
self
}

Expand Down Expand Up @@ -249,14 +256,14 @@ where
/// ```
pub fn validate_with<V>(mut self, mut validator: V) -> Self
where
V: Validator<T> + 'a,
V: InputValidator<T> + 'a,
V::Err: ToString,
{
let mut old_validator_func = self.validator.take();

self.validator = Some(Box::new(move |value: &T| -> Option<String> {
self.validator = Some(Arc::new(Mutex::new(move |value: &T| -> Option<String> {
if let Some(old) = old_validator_func.as_mut() {
if let Some(err) = old(value) {
if let Some(err) = old.lock().unwrap()(value) {
return Some(err);
}
}
Expand All @@ -265,7 +272,7 @@ where
Ok(()) => None,
Err(err) => Some(err.to_string()),
}
}));
})));

self
}
Expand Down Expand Up @@ -491,7 +498,7 @@ where
Key::ArrowUp => {
let line_size = term.size().1 as usize;
if let Some(history) = &self.history {
if let Some(previous) = history.read(hist_pos) {
if let Some(previous) = history.lock().unwrap().read(hist_pos) {
hist_pos += 1;
let mut chars_len = chars.len();
while ((prompt_len + chars_len) / line_size) > 0 {
Expand Down Expand Up @@ -550,7 +557,7 @@ where
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) {
if let Some(previous) = history.lock().unwrap().read(pos) {
for ch in previous.chars() {
chars.insert(position, ch);
position += 1;
Expand All @@ -574,7 +581,7 @@ where
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) {
if let Some(err) = validator.lock().unwrap()(default) {
render.error(&err)?;
continue;
}
Expand All @@ -594,11 +601,11 @@ where
Ok(value) => {
#[cfg(feature = "history")]
if let Some(history) = &mut self.history {
history.write(&value);
history.lock().unwrap().write(&value);
}

if let Some(ref mut validator) = self.validator {
if let Some(err) = validator(&value) {
if let Some(err) = validator.lock().unwrap()(&value) {
render.error(&err)?;
continue;
}
Expand Down Expand Up @@ -675,7 +682,7 @@ where
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) {
if let Some(err) = validator.lock().unwrap()(default) {
render.error(&err)?;
continue;
}
Expand All @@ -694,7 +701,7 @@ where
match input.parse::<T>() {
Ok(value) => {
if let Some(ref mut validator) = self.validator {
if let Some(err) = validator(&value) {
if let Some(err) = validator.lock().unwrap()(&value) {
render.error(&err)?;
continue;
}
Expand All @@ -715,3 +722,15 @@ where
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_clone() {
let input = Input::<String>::new().with_prompt("Your name");

let _ = input.clone();
}
}
13 changes: 13 additions & 0 deletions src/prompts/multi_select.rs
Expand Up @@ -30,6 +30,7 @@ use crate::{
/// }
/// }
/// ```
#[derive(Clone)]
pub struct MultiSelect<'a> {
defaults: Vec<bool>,
items: Vec<String>,
Expand Down Expand Up @@ -370,3 +371,15 @@ impl<'a> MultiSelect<'a> {
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_clone() {
let multi_select = MultiSelect::new().with_prompt("Select your favorite(s)");

let _ = multi_select.clone();
}
}

0 comments on commit 9baa851

Please sign in to comment.