Repository: mikaelmello/inquire (Rust)

Issue:
Suppressing static lifetimes from Text (and other modules ?)
**Is your feature request related to a problem? Please describe.**
While working with Inquire to setup a name verification, I look through an HashMap that is contained by a struct and pass that struct through a ref, that looks like this :

```rs
struct Container {
   ids: HashMap<String, usize>,
}

fn type_id(container: &Container) -> InquireResult<String> {
   Text::new("type the id:")
       .with_validator(|text:&str| {
           // Validation process...
           if container.ids.contains(text) {
               Validation::Invalid("nope, exists".into())
           } else {
              Validation::Valid
           }
      }).prompt()
}
```

But I can't pass a simple ref to the validator as it needs a static lifetime, and I rather avoid cloning the hashmap / container (they can get pretty big) or sharing them through an `Rc<RefCell<T>>`.

**Describe the solution you'd like**
I tried to implement a solution which is available on my fork : https://github.com/stormshield-hf/inquire/commit/2a07dc44a84534f2023a2a41c40cf8ca5afd0fcd (branch in case I end up doing other changes : https://github.com/stormshield-hf/inquire/tree/work/hf/remove_static_lifetimes)

TLDR: We add a second lifetime to the `Text` struct which is associated to validators (and the autocompleter) and add a clone implementation that specializes on `'b: static`.

The advantage of this solution is that it will keep the existing clone impl.
The main disadvantage is that it will be a breaking change as we need to add a lifetime.

**Describe alternatives you've considered**
Another solution we tried is specifying that validator must be `'a` rather than `'static` but we must specialize the `Clone` as `'a: static`, which might be an even greater breaking change depending on the usage of the lib.

**Additional context**
Add any other context or screenshots about the feature request here.



Discussion:
Hey! 
Ran across the same issue, having a 'static lifetime when wanting to validate something living in the AppState, as in my case, it's not ideal. Ended up cloning as the data is not very big.
If there's a possibility to change that to a simple lifetime it would be great!
> Hey! Ran across the same issue, having a 'static lifetime when wanting to validate something living in the AppState, as in my case, it's not ideal. Ended up cloning as the data is not very big. If there's a possibility to change that to a simple lifetime it would be great!

@NicolasGB I have the same error how did you clone it`?

My fn looks like this:
```
add_profile(config.profiles.keys().clone().map(|key| key.as_str()).collect().clone(), config.profiles)


fn add_profile(choices: Vec<&str>, mut profiles: HashMap<String, Profile>) {
    let _name = Text::new("Name:")
        .with_validator(required!("This field is required"))
        .with_validator(
            move |name: &str| {
            if choices.iter().any(|e| name.contains(e)) {
                Ok(Validation::Invalid("not exists".into()))
            } else {
                Ok(Validation::Valid)
            }
        })
        .with_help_message("Optional notes")
        .prompt();

    let _activate = Confirm::new("Do you want to activate the profile?")
        .with_default(false)
        .prompt();
}
```
Hey @Yingrjimsch, sorry i was away for the last few weeks.

I did pretty much  the same you did, i had to validate if a given data already existed in my appstate, and had to clone my vec.  As the data is not very big i didn't bother wrapping in Rc or whatsoever.

You can have a look at my code here: https://github.com/NicolasGB/filecrab/blob/main/filecrab-cli/src/cli/config.rs
On line 125 you'll see the validation closure.

Hope it helps :)

I am sharing a certain object via `Rc` used for validation and then reclaiming it back after validation. Seems a bit hacky that I have to do that and unwrap. Would be great to remove the `'static` requirement, also considering that `Text` has `'a` lifetime, maybe we can pin on that?



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 22879e76..6f2f497a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 
 ## [Unreleased] <!-- ReleaseDate -->
 
+- Don't require static lifetime for autocompleter and validator, thanks @stormshield-guillaumed (#272)!
 - Upgraded `crossterm` to 0.28.1.
 - Raised minimum supported Rust version to 1.80.0.
 - Migrate functionality from `once_cell` to `stdlib`. Thanks @jarjk for cutting down on a dependency!
diff --git a/inquire/src/prompts/text/config.rs b/inquire/src/prompts/text/config.rs
index d0b84fa3..fad2a75e 100644
--- a/inquire/src/prompts/text/config.rs
+++ b/inquire/src/prompts/text/config.rs
@@ -7,8 +7,8 @@ pub struct TextConfig {
     pub page_size: usize,
 }
 
-impl From<&Text<'_>> for TextConfig {
-    fn from(value: &Text<'_>) -> Self {
+impl From<&Text<'_, '_>> for TextConfig {
+    fn from(value: &Text<'_, '_>) -> Self {
         Self {
             page_size: value.page_size,
         }
diff --git a/inquire/src/prompts/text/mod.rs b/inquire/src/prompts/text/mod.rs
index aeda72d2..1ddd26be 100644
--- a/inquire/src/prompts/text/mod.rs
+++ b/inquire/src/prompts/text/mod.rs
@@ -73,8 +73,7 @@ const DEFAULT_HELP_MESSAGE_WITH_AC: &str = "↑↓ to move, tab to autocomplete,
 ///     Err(_) => println!("An error happened when asking for your name, try again later."),
 /// }
 /// ```
-#[derive(Clone)]
-pub struct Text<'a> {
+pub struct Text<'a, 'b> {
     /// Message to be presented to the user.
     pub message: &'a str,
 
@@ -98,7 +97,7 @@ pub struct Text<'a> {
     pub formatter: StringFormatter<'a>,
 
     /// Autocompleter responsible for handling suggestions and input completions.
-    pub autocompleter: Option<Box<dyn Autocomplete>>,
+    pub autocompleter: Option<Box<dyn Autocomplete + 'b>>,
 
     /// Collection of validators to apply to the user input.
     ///
@@ -106,7 +105,7 @@ pub struct Text<'a> {
     /// only the first validation error that might appear.
     ///
     /// The possible error is displayed to the user one line above the prompt.
-    pub validators: Vec<Box<dyn StringValidator>>,
+    pub validators: Vec<Box<dyn StringValidator + 'b>>,
 
     /// Page size of the suggestions displayed to the user, when applicable.
     pub page_size: usize,
@@ -122,7 +121,24 @@ pub struct Text<'a> {
     pub render_config: RenderConfig<'a>,
 }
 
-impl<'a> Text<'a> {
+impl<'a> Clone for Text<'a, 'static> {
+    fn clone(&self) -> Self {
+        Self {
+            message: self.message,
+            initial_value: self.initial_value,
+            default: self.default,
+            placeholder: self.placeholder,
+            help_message: self.help_message,
+            formatter: self.formatter,
+            autocompleter: self.autocompleter.clone(),
+            validators: self.validators.clone(),
+            page_size: self.page_size,
+            render_config: self.render_config,
+        }
+    }
+}
+
+impl<'a, 'b> Text<'a, 'b> {
     /// Default formatter, set to [DEFAULT_STRING_FORMATTER](crate::formatter::DEFAULT_STRING_FORMATTER)
     pub const DEFAULT_FORMATTER: StringFormatter<'a> = DEFAULT_STRING_FORMATTER;
 
@@ -182,7 +198,7 @@ impl<'a> Text<'a> {
     /// Sets a new autocompleter
     pub fn with_autocomplete<AC>(mut self, ac: AC) -> Self
     where
-        AC: Autocomplete + 'static,
+        AC: Autocomplete + 'b,
     {
         self.autocompleter = Some(Box::new(ac));
         self
@@ -210,7 +226,7 @@ impl<'a> Text<'a> {
     /// The possible error is displayed to the user one line above the prompt.
     pub fn with_validator<V>(mut self, validator: V) -> Self
     where
-        V: StringValidator + 'static,
+        V: StringValidator + 'b,
     {
         self.validators.push(Box::new(validator));
         self
@@ -226,7 +242,6 @@ impl<'a> Text<'a> {
     /// The possible error is displayed to the user one line above the prompt.
     pub fn with_validators(mut self, validators: &[Box<dyn StringValidator>]) -> Self {
         for validator in validators {
-            #[allow(suspicious_double_ref_op)]
             self.validators.push(validator.clone());
         }
         self
diff --git a/inquire/src/prompts/text/prompt.rs b/inquire/src/prompts/text/prompt.rs
index e0ae7ab9..b5701a5a 100644
--- a/inquire/src/prompts/text/prompt.rs
+++ b/inquire/src/prompts/text/prompt.rs
@@ -15,22 +15,22 @@ use crate::{
 
 use super::{action::TextPromptAction, config::TextConfig, DEFAULT_HELP_MESSAGE_WITH_AC};
 
-pub struct TextPrompt<'a> {
+pub struct TextPrompt<'a, 'b> {
     message: &'a str,
     config: TextConfig,
     default: Option<&'a str>,
     help_message: Option<&'a str>,
     input: Input,
     formatter: StringFormatter<'a>,
-    validators: Vec<Box<dyn StringValidator>>,
+    validators: Vec<Box<dyn StringValidator + 'b>>,
     error: Option<ErrorMessage>,
-    autocompleter: Box<dyn Autocomplete>,
+    autocompleter: Box<dyn Autocomplete + 'b>,
     suggested_options: Vec<String>,
     suggestion_cursor_index: Option<usize>,
 }
 
-impl<'a> From<Text<'a>> for TextPrompt<'a> {
-    fn from(so: Text<'a>) -> Self {
+impl<'a, 'b> From<Text<'a, 'b>> for TextPrompt<'a, 'b> {
+    fn from(so: Text<'a, 'b>) -> Self {
         let input = Input::new_with(so.initial_value.unwrap_or_default());
         let input = if let Some(placeholder) = so.placeholder {
             input.with_placeholder(placeholder)
@@ -56,13 +56,13 @@ impl<'a> From<Text<'a>> for TextPrompt<'a> {
     }
 }
 
-impl<'a> From<&'a str> for Text<'a> {
+impl<'a, 'b> From<&'a str> for Text<'a, 'b> {
     fn from(val: &'a str) -> Self {
         Text::new(val)
     }
 }
 
-impl<'a> TextPrompt<'a> {
+impl<'a, 'b> TextPrompt<'a, 'b> {
     fn update_suggestions(&mut self) -> InquireResult<()> {
         self.suggested_options = self.autocompleter.get_suggestions(self.input.content())?;
         self.suggestion_cursor_index = None;
@@ -161,7 +161,7 @@ impl<'a> TextPrompt<'a> {
     }
 }
 
-impl<'a, Backend> Prompt<Backend> for TextPrompt<'a>
+impl<'a, 'b, Backend> Prompt<Backend> for TextPrompt<'a, 'b>
 where
     Backend: TextBackend,
 {


Test patch:
diff --git a/inquire/src/prompts/text/test.rs b/inquire/src/prompts/text/test.rs
index 35d43e58..17051733 100644
--- a/inquire/src/prompts/text/test.rs
+++ b/inquire/src/prompts/text/test.rs
@@ -2,7 +2,7 @@ use super::Text;
 use crate::ui::{Key, KeyModifiers};
 use crate::validator::{ErrorMessage, Validation};
 
-fn default<'a>() -> Text<'a> {
+fn default<'a, 'b>() -> Text<'a, 'b> {
     Text::new("Question?")
 }