diff --git a/CHANGELOG.md b/CHANGELOG.md index 3235fdfc..1e5c5be1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 0.8.0 + +### Enhancements + +* `Input::validate_with` can take a `FnMut` (allowing multiple references) + +### Breaking + +* `Input::interact*` methods take `&mut self` instead of `&self` + ## 0.7.0 ### Enhancements diff --git a/examples/input.rs b/examples/input.rs index d9e4ce58..8691c2e4 100644 --- a/examples/input.rs +++ b/examples/input.rs @@ -10,11 +10,15 @@ fn main() { let mail: String = Input::with_theme(&ColorfulTheme::default()) .with_prompt("Your email") - .validate_with(|input: &String| -> Result<(), &str> { - if input.contains('@') { - Ok(()) - } else { - Err("This is not a mail address") + .validate_with({ + let mut force = None; + move |input: &String| -> Result<(), &str> { + if input.contains('@') || force.as_ref().map_or(false, |old| old == input) { + Ok(()) + } else { + force = Some(input.clone()); + Err("This is not a mail address; type the same value again to force use") + } } }) .interact_text() diff --git a/src/prompts/input.rs b/src/prompts/input.rs index 34fbe3f1..de87c36e 100644 --- a/src/prompts/input.rs +++ b/src/prompts/input.rs @@ -44,7 +44,7 @@ pub struct Input<'a, T> { initial_text: Option, theme: &'a dyn Theme, permit_empty: bool, - validator: Option Option + 'a>>, + validator: Option Option + 'a>>, } impl<'a, T> Default for Input<'a, T> @@ -139,15 +139,15 @@ where /// .interact() /// .unwrap(); /// ``` - pub fn validate_with(&mut self, validator: V) -> &mut Input<'a, T> + pub fn validate_with(&mut self, mut validator: V) -> &mut Input<'a, T> where V: Validator + 'a, T: 'a, { - let old_validator_func = self.validator.take(); + let mut old_validator_func = self.validator.take(); self.validator = Some(Box::new(move |value: &T| -> Option { - if let Some(old) = old_validator_func.as_ref() { + if let Some(old) = old_validator_func.as_mut() { if let Some(err) = old(value) { return Some(err); } @@ -168,12 +168,12 @@ where /// 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(&self) -> io::Result { + pub fn interact_text(&mut self) -> io::Result { 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(&self, term: &Term) -> io::Result { + pub fn interact_text_on(&mut self, term: &Term) -> io::Result { let mut render = TermThemeRenderer::new(term, self.theme); loop { @@ -265,7 +265,7 @@ where match input.parse::() { Ok(value) => { - if let Some(ref validator) = self.validator { + if let Some(ref mut validator) = self.validator { if let Some(err) = validator(&value) { render.error(&err)?; continue; @@ -293,12 +293,12 @@ where /// /// If the user confirms the result is `true`, `false` otherwise. /// The dialog is rendered on stderr. - pub fn interact(&self) -> io::Result { + pub fn interact(&mut self) -> io::Result { self.interact_on(&Term::stderr()) } /// Like [`interact`](#method.interact) but allows a specific terminal to be set. - pub fn interact_on(&self, term: &Term) -> io::Result { + pub fn interact_on(&mut self, term: &Term) -> io::Result { let mut render = TermThemeRenderer::new(term, self.theme); loop { @@ -336,7 +336,7 @@ where match input.parse::() { Ok(value) => { - if let Some(ref validator) = self.validator { + if let Some(ref mut validator) = self.validator { if let Some(err) = validator(&value) { render.error(&err)?; continue; diff --git a/src/validate.rs b/src/validate.rs index e716b13b..080b5b04 100644 --- a/src/validate.rs +++ b/src/validate.rs @@ -12,13 +12,13 @@ pub trait Validator { /// /// If this produces `Ok(())` then the value is used and parsed, if /// an error is returned validation fails with that error. - fn validate(&self, input: &T) -> Result<(), Self::Err>; + fn validate(&mut self, input: &T) -> Result<(), Self::Err>; } -impl Result<(), E>, E: Debug + Display> Validator for F { +impl Result<(), E>, E: Debug + Display> Validator for F { type Err = E; - fn validate(&self, input: &T) -> Result<(), Self::Err> { + fn validate(&mut self, input: &T) -> Result<(), Self::Err> { self(input) } }