1
1
use std:: io;
2
2
3
- use crate :: theme:: { SimpleTheme , TermThemeRenderer , Theme } ;
3
+ use crate :: {
4
+ theme:: { SimpleTheme , TermThemeRenderer , Theme } ,
5
+ validate:: PasswordValidator ,
6
+ } ;
4
7
5
8
use console:: Term ;
6
9
use zeroize:: Zeroizing ;
7
10
11
+ type PasswordValidatorCallback < ' a > = Box < dyn Fn ( & String ) -> Option < String > + ' a > ;
12
+
8
13
/// Renders a password input prompt.
9
14
///
10
15
/// ## Example usage
@@ -25,6 +30,7 @@ pub struct Password<'a> {
25
30
theme : & ' a dyn Theme ,
26
31
allow_empty_password : bool ,
27
32
confirmation_prompt : Option < ( String , String ) > ,
33
+ validator : Option < PasswordValidatorCallback < ' a > > ,
28
34
}
29
35
30
36
impl Default for Password < ' static > {
@@ -40,7 +46,7 @@ impl Password<'static> {
40
46
}
41
47
}
42
48
43
- impl Password < ' _ > {
49
+ impl < ' a > Password < ' a > {
44
50
/// Sets the password input prompt.
45
51
pub fn with_prompt < S : Into < String > > ( & mut self , prompt : S ) -> & mut Self {
46
52
self . prompt = prompt. into ( ) ;
@@ -73,6 +79,47 @@ impl Password<'_> {
73
79
self
74
80
}
75
81
82
+ /// Registers a validator.
83
+ ///
84
+ /// # Example
85
+ ///
86
+ /// ```no_run
87
+ /// # use dialoguer::Password;
88
+ /// let password: String = Password::new()
89
+ /// .with_prompt("Enter password")
90
+ /// .validate_with(|input: &String| -> Result<(), &str> {
91
+ /// if input.len() > 8 {
92
+ /// Ok(())
93
+ /// } else {
94
+ /// Err("Password must be longer than 8")
95
+ /// }
96
+ /// })
97
+ /// .interact()
98
+ /// .unwrap();
99
+ /// ```
100
+ pub fn validate_with < V > ( & mut self , validator : V ) -> & mut Self
101
+ where
102
+ V : PasswordValidator + ' a ,
103
+ V :: Err : ToString ,
104
+ {
105
+ let old_validator_func = self . validator . take ( ) ;
106
+
107
+ self . validator = Some ( Box :: new ( move |value : & String | -> Option < String > {
108
+ if let Some ( old) = & old_validator_func {
109
+ if let Some ( err) = old ( value) {
110
+ return Some ( err) ;
111
+ }
112
+ }
113
+
114
+ match validator. validate ( value) {
115
+ Ok ( ( ) ) => None ,
116
+ Err ( err) => Some ( err. to_string ( ) ) ,
117
+ }
118
+ } ) ) ;
119
+
120
+ self
121
+ }
122
+
76
123
/// Enables user interaction and returns the result.
77
124
///
78
125
/// If the user confirms the result is `true`, `false` otherwise.
@@ -89,28 +136,30 @@ impl Password<'_> {
89
136
loop {
90
137
let password = Zeroizing :: new ( self . prompt_password ( & mut render, & self . prompt ) ?) ;
91
138
139
+ if let Some ( ref validator) = self . validator {
140
+ if let Some ( err) = validator ( & password) {
141
+ render. error ( & err) ?;
142
+ continue ;
143
+ }
144
+ }
145
+
92
146
if let Some ( ( ref prompt, ref err) ) = self . confirmation_prompt {
93
147
let pw2 = Zeroizing :: new ( self . prompt_password ( & mut render, prompt) ?) ;
94
148
95
- if * password == * pw2 {
96
- render. clear ( ) ?;
97
- if self . report {
98
- render. password_prompt_selection ( & self . prompt ) ?;
99
- }
100
- term. flush ( ) ?;
101
- return Ok ( ( * password) . clone ( ) ) ;
149
+ if * password != * pw2 {
150
+ render. error ( err) ?;
151
+ continue ;
102
152
}
153
+ }
103
154
104
- render. error ( err) ?;
105
- } else {
106
- render. clear ( ) ?;
107
- if self . report {
108
- render. password_prompt_selection ( & self . prompt ) ?;
109
- }
110
- term. flush ( ) ?;
155
+ render. clear ( ) ?;
111
156
112
- return Ok ( ( * password) . clone ( ) ) ;
157
+ if self . report {
158
+ render. password_prompt_selection ( & self . prompt ) ?;
113
159
}
160
+ term. flush ( ) ?;
161
+
162
+ return Ok ( ( * password) . clone ( ) ) ;
114
163
}
115
164
}
116
165
@@ -139,6 +188,7 @@ impl<'a> Password<'a> {
139
188
theme,
140
189
allow_empty_password : false ,
141
190
confirmation_prompt : None ,
191
+ validator : None ,
142
192
}
143
193
}
144
194
}
0 commit comments