1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use std::io;
use crate::theme::{SimpleTheme, TermThemeRenderer, Theme};
use console::Term;
pub struct Password<'a> {
prompt: String,
theme: &'a dyn Theme,
allow_empty_password: bool,
confirmation_prompt: Option<(String, String)>,
}
impl<'a> Default for Password<'a> {
fn default() -> Password<'a> {
Password::new()
}
}
impl<'a> Password<'a> {
pub fn new() -> Password<'static> {
Password::with_theme(&SimpleTheme)
}
pub fn with_theme(theme: &'a dyn Theme) -> Password<'a> {
Password {
prompt: "".into(),
theme,
allow_empty_password: false,
confirmation_prompt: None,
}
}
pub fn with_prompt<S: Into<String>>(&mut self, prompt: S) -> &mut Password<'a> {
self.prompt = prompt.into();
self
}
pub fn with_confirmation<A, B>(&mut self, prompt: A, mismatch_err: B) -> &mut Password<'a>
where
A: Into<String>,
B: Into<String>,
{
self.confirmation_prompt = Some((prompt.into(), mismatch_err.into()));
self
}
pub fn allow_empty_password(&mut self, allow_empty_password: bool) -> &mut Password<'a> {
self.allow_empty_password = allow_empty_password;
self
}
pub fn interact(&self) -> io::Result<String> {
self.interact_on(&Term::stderr())
}
pub fn interact_on(&self, term: &Term) -> io::Result<String> {
let mut render = TermThemeRenderer::new(term, self.theme);
render.set_prompts_reset_height(false);
loop {
let password = self.prompt_password(&mut render, &self.prompt)?;
if let Some((ref prompt, ref err)) = self.confirmation_prompt {
let pw2 = self.prompt_password(&mut render, &prompt)?;
if password == pw2 {
render.clear()?;
render.password_prompt_selection(&self.prompt)?;
term.flush()?;
return Ok(password);
}
render.error(err)?;
} else {
render.clear()?;
render.password_prompt_selection(&self.prompt)?;
term.flush()?;
return Ok(password);
}
}
}
fn prompt_password(&self, render: &mut TermThemeRenderer, prompt: &str) -> io::Result<String> {
loop {
render.password_prompt(prompt)?;
render.term().flush()?;
let input = render.term().read_secure_line()?;
render.add_line();
if !input.is_empty() || self.allow_empty_password {
return Ok(input);
}
}
}
}