inquire/prompts/password/
prompt.rs1use crate::{
2 error::InquireResult,
3 formatter::StringFormatter,
4 input::Input,
5 prompts::prompt::{ActionResult, Prompt},
6 ui::PasswordBackend,
7 validator::{ErrorMessage, StringValidator, Validation},
8 InquireError, Password, PasswordDisplayMode,
9};
10
11use super::{action::PasswordPromptAction, config::PasswordConfig};
12
13struct PasswordConfirmation<'a> {
15 pub message: &'a str,
17
18 pub error_message: &'a str,
20
21 pub input: Input,
23}
24
25pub struct PasswordPrompt<'a> {
26 message: &'a str,
27 config: PasswordConfig,
28 help_message: Option<&'a str>,
29 input: Input,
30 current_mode: PasswordDisplayMode,
31 confirmation: Option<PasswordConfirmation<'a>>, confirmation_stage: bool,
33 formatter: StringFormatter<'a>,
34 validators: Vec<Box<dyn StringValidator>>,
35 error: Option<ErrorMessage>,
36}
37
38impl<'a> From<Password<'a>> for PasswordPrompt<'a> {
39 fn from(so: Password<'a>) -> Self {
40 let confirmation = match so.enable_confirmation {
41 true => Some(PasswordConfirmation {
42 message: so.custom_confirmation_message.unwrap_or("Confirmation:"),
43 error_message: so
44 .custom_confirmation_error_message
45 .unwrap_or("The answers don't match."),
46 input: Input::new(),
47 }),
48 false => None,
49 };
50
51 Self {
52 message: so.message,
53 config: (&so).into(),
54 help_message: so.help_message,
55 current_mode: so.display_mode,
56 confirmation,
57 confirmation_stage: false,
58 formatter: so.formatter,
59 validators: so.validators,
60 input: Input::new(),
61 error: None,
62 }
63 }
64}
65
66impl<'a> From<&'a str> for Password<'a> {
67 fn from(val: &'a str) -> Self {
68 Password::new(val)
69 }
70}
71
72impl<'a> PasswordPrompt<'a> {
73 fn active_input_mut(&mut self) -> &mut Input {
74 if let Some(c) = &mut self.confirmation {
75 if self.confirmation_stage {
76 return &mut c.input;
77 }
78 }
79
80 &mut self.input
81 }
82
83 fn toggle_display_mode(&mut self) -> ActionResult {
84 let new_mode = match self.current_mode {
85 PasswordDisplayMode::Hidden | PasswordDisplayMode::Masked => PasswordDisplayMode::Full,
86 PasswordDisplayMode::Full => self.config.display_mode,
87 };
88
89 if new_mode != self.current_mode {
90 self.current_mode = new_mode;
91 ActionResult::NeedsRedraw
92 } else {
93 ActionResult::Clean
94 }
95 }
96
97 fn confirmation_step(&mut self) -> ConfirmationStepResult {
98 let cur_answer = self.cur_answer().to_owned();
99 match &mut self.confirmation {
100 None => ConfirmationStepResult::NoConfirmationRequired,
101 Some(confirmation) => {
102 if self.confirmation_stage {
103 if cur_answer == confirmation.input.content() {
104 ConfirmationStepResult::ConfirmationValidated
105 } else {
106 self.confirmation_stage = false;
107 confirmation.input.clear();
108 ConfirmationStepResult::ConfirmationInvalidated(ErrorMessage::Custom(
109 confirmation.error_message.to_owned(),
110 ))
111 }
112 } else {
113 confirmation.input.clear();
114 self.confirmation_stage = true;
115
116 ConfirmationStepResult::ConfirmationPending
117 }
118 }
119 }
120 }
121
122 fn validate_current_answer(&self) -> InquireResult<Validation> {
123 for validator in &self.validators {
124 match validator.validate(self.cur_answer()) {
125 Ok(Validation::Valid) => {}
126 Ok(Validation::Invalid(msg)) => return Ok(Validation::Invalid(msg)),
127 Err(err) => return Err(InquireError::Custom(err)),
128 }
129 }
130
131 Ok(Validation::Valid)
132 }
133
134 fn cur_answer(&self) -> &str {
135 self.input.content()
136 }
137}
138
139impl<'a, Backend> Prompt<Backend> for PasswordPrompt<'a>
140where
141 Backend: PasswordBackend,
142{
143 type Config = PasswordConfig;
144 type InnerAction = PasswordPromptAction;
145 type Output = String;
146
147 fn message(&self) -> &str {
148 self.message
149 }
150
151 fn config(&self) -> &PasswordConfig {
152 &self.config
153 }
154
155 fn format_answer(&self, answer: &String) -> String {
156 (self.formatter)(answer)
157 }
158
159 fn pre_cancel(&mut self) -> InquireResult<bool> {
160 if let Some(confirmation) = &mut self.confirmation {
161 if self.confirmation_stage {
162 confirmation.input.clear();
163 self.confirmation_stage = false;
164 return Ok(false);
165 }
166 }
167
168 Ok(true)
169 }
170
171 fn submit(&mut self) -> InquireResult<Option<String>> {
172 if let Validation::Invalid(msg) = self.validate_current_answer()? {
173 self.error = Some(msg);
174 if self.config.display_mode == PasswordDisplayMode::Hidden {
175 self.input.clear();
176 }
177 return Ok(None);
178 }
179
180 let confirmation = self.confirmation_step();
181
182 let cur_answer = self.cur_answer().to_owned();
183
184 let result = match confirmation {
185 ConfirmationStepResult::NoConfirmationRequired
186 | ConfirmationStepResult::ConfirmationValidated => Some(cur_answer),
187 ConfirmationStepResult::ConfirmationPending => None,
188 ConfirmationStepResult::ConfirmationInvalidated(message) => {
189 self.error = Some(message);
190 self.input.clear();
191 None
192 }
193 };
194
195 Ok(result)
196 }
197
198 fn handle(&mut self, action: PasswordPromptAction) -> InquireResult<ActionResult> {
199 let result = match action {
200 PasswordPromptAction::ValueInput(input_action) => {
201 self.active_input_mut().handle(input_action).into()
202 }
203 PasswordPromptAction::ToggleDisplayMode => self.toggle_display_mode(),
204 };
205
206 Ok(result)
207 }
208
209 fn render(&self, backend: &mut Backend) -> InquireResult<()> {
210 if let Some(err) = &self.error {
211 backend.render_error_message(err)?;
212 }
213
214 match self.current_mode {
215 PasswordDisplayMode::Hidden => {
216 backend.render_prompt(self.message)?;
217
218 match &self.confirmation {
219 Some(confirmation) if self.confirmation_stage => {
220 backend.render_prompt(confirmation.message)?;
221 }
222 _ => {}
223 }
224 }
225 PasswordDisplayMode::Masked => {
226 backend.render_prompt_with_masked_input(self.message, &self.input)?;
227
228 match &self.confirmation {
229 Some(confirmation) if self.confirmation_stage => {
230 backend.render_prompt_with_masked_input(
231 confirmation.message,
232 &confirmation.input,
233 )?;
234 }
235 _ => {}
236 }
237 }
238 PasswordDisplayMode::Full => {
239 backend.render_prompt_with_full_input(self.message, &self.input)?;
240
241 match &self.confirmation {
242 Some(confirmation) if self.confirmation_stage => {
243 backend.render_prompt_with_full_input(
244 confirmation.message,
245 &confirmation.input,
246 )?;
247 }
248 _ => {}
249 }
250 }
251 }
252
253 if let Some(message) = self.help_message {
254 backend.render_help_message(message)?;
255 }
256
257 Ok(())
258 }
259}
260
261#[derive(Debug, Clone, PartialEq, Eq)]
262pub enum ConfirmationStepResult {
263 NoConfirmationRequired,
264 ConfirmationPending,
265 ConfirmationValidated,
266 ConfirmationInvalidated(ErrorMessage),
267}